For R beginners

New chunk Ctrl+Alt+I

Execute chunk Ctrl+Shift+Enter

Execute all chunks Ctrl+Alt+R

HTML preview Ctrl+Shift+K

Library preparations

library(readr)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ggplot2)
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
library(stats)
library(tm)
Loading required package: NLP

Attaching package: ‘NLP’

The following object is masked from ‘package:ggplot2’:

    annotate
library(text2vec)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(textstem)
Loading required package: koRpus.lang.en
Loading required package: koRpus
Loading required package: sylly
For information on available language packages for 'koRpus', run

  available.koRpus.lang()

and see ?install.koRpus.lang()


Attaching package: ‘koRpus’

The following object is masked from ‘package:tm’:

    readTagged

The following object is masked from ‘package:readr’:

    tokenize
library(syuzhet)

Data Import

data_posts <- read.csv("~/4year/2semester/dtII/CSVs/HEIs.csv",
                 colClasses = c(tweet_id = "character"))

# Modifying created_at type so that attribute can be used more easily 
data_posts$created_at <- as.POSIXct(data_posts$created_at,
                              format= "%Y-%m-%dT%H:%M:%S", tz="UTC")

#View(data)
summary(data_posts)
      id              tweet_id             text               type           bookmark_count    favorite_count     retweet_count      reply_count      
 Length:11728       Length:11728       Length:11728       Length:11728       Min.   :  0.000   Min.   :    0.00   Min.   :   0.00   Min.   :   0.000  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:  0.000   1st Qu.:    7.00   1st Qu.:   2.00   1st Qu.:   0.000  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :  0.000   Median :   20.00   Median :   5.00   Median :   1.000  
                                                                             Mean   :  1.543   Mean   :   60.67   Mean   :  10.62   Mean   :   3.888  
                                                                             3rd Qu.:  1.000   3rd Qu.:   57.00   3rd Qu.:  11.00   3rd Qu.:   3.000  
                                                                             Max.   :418.000   Max.   :41655.00   Max.   :4214.00   Max.   :2317.000  
                                                                                                                                                      
   view_count        created_at                       hashtags             urls            media_type         media_urls       
 Min.   :      5   Min.   :2022-08-01 03:05:11.00   Length:11728       Length:11728       Length:11728       Length:11728      
 1st Qu.:   2643   1st Qu.:2022-10-19 12:56:27.00   Class :character   Class :character   Class :character   Class :character  
 Median :   6240   Median :2023-01-29 08:26:30.00   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :  14182   Mean   :2023-01-30 07:39:34.96                                                                              
 3rd Qu.:  16058   3rd Qu.:2023-05-05 14:16:43.25                                                                              
 Max.   :7604544   Max.   :2023-08-31 20:50:01.00                                                                              
 NA's   :4840                                                                                                                  

Initial Data Preparation

# Count of how many entries each HEI has
number_posts <- data_posts %>%
              group_by(id) %>% summarise(count = n())

number_posts

Since complutense only has 1 entry we can’t learn anything from it, so we removed it

data_posts <- data_posts[data_posts$id != "complutense.csv", ]

Visualization of all posts, just tweets and just replies

number_posts <- data_posts %>%
              group_by(id) %>% summarise(posts = n())

number_tweets <- data_posts[data_posts$type == "Tweet", ] %>%
              group_by(id) %>% summarise(tweets = n())

number_replies <- data_posts[data_posts$type == "Reply", ] %>%
              group_by(id) %>% summarise(replies = n())

print(number_posts)
print(number_tweets)
print(number_replies)

Calculating the percentage of tweets and replies based on all posts

# Merging the counts of tweets and replies with the count of posts
data_ratio <- merge(number_posts, number_tweets, by = "id", all = TRUE)
data_ratio <- merge(data_ratio, number_replies, by = "id", all = TRUE)


data_ratio$percentage_tweets <- round(((data_ratio$tweets / data_ratio$posts) * 100), 2)
data_ratio$percentage_replies <- round(((data_ratio$replies / data_ratio$posts) * 100), 2)

data_ratio <- data_ratio[, c("id", "percentage_tweets", "percentage_replies")]

data_ratio$percentage_replies[is.na(data_ratio$percentage_replies)] <- 0

print(data_ratio)

NA removal

Function to visualize the number of NAs in all columns

na_count <- function(){
  # Counting the number of NA values for each column
  na_count <- colSums(is.na(data_posts))
  
  # Creating a new data frame with the NA counts
  na_counts_table <- data.frame(Column = names(na_count), NA_Count = na_count)
  
  print(na_counts_table)
}

Calculations of view, favourite, retweet and reply percentiles and visualization of NAs in all columns

data_posts <- data_posts %>%
  group_by(id) %>%
  mutate(view_percentile = ntile(view_count, 100),
         favorite_percentile = ntile(favorite_count, 100),
         retweet_percentile = ntile(retweet_count, 100),
         reply_percentile = ntile(reply_count, 100)) %>%
  rowwise() %>%
  mutate(avg_percentile = round(mean(c(view_percentile, favorite_percentile, retweet_percentile, reply_percentile), na.rm = TRUE), 2))

na_count()

data_percentile <- data_posts[, c("id", "view_percentile", "favorite_percentile", "retweet_percentile", "reply_percentile", "avg_percentile")]

print(data_percentile)

Calculation of the maximum number of views for each HEI

max_view_counts <- tapply(data_posts$view_count, data_posts$id, max, na.rm = TRUE)

print(max_view_counts)
      duke.csv       epfl.csv        goe.csv    harvard.csv  leicester.csv manchester.csv        mit.csv         sb.csv   stanford.csv    trinity.csv 
        307969         105095          15455        2982704          47838         317086        7604544         607498         222593         205333 
        wv.csv       yale.csv 
        109265         143108 

Removal of NAs

# From view count
data_posts$view_count <- ifelse(
  is.na(data_posts$view_count),
  round(max_view_counts[data_posts$id] * (data_posts$avg_percentile / 100)),
  data_posts$view_count)

# From view percentile
data_posts$view_percentile <- ifelse(
  is.na(data_posts$view_percentile),
  data_posts$avg_percentile,
  data_posts$view_percentile)

Visualization of NAs in all columns

na_count()

Function to calculate average posts

average_posts <- function(timeframe){
  # Calculation of the timeframe between earliest and latest post for each HEI
  date_range <- data_posts %>%
    group_by(id) %>%
    summarise(min_date = min(created_at),
              max_date = max(created_at)) %>%
    mutate(num_days = as.numeric(difftime(max_date, min_date, units = timeframe)))
  
  # Naming the column respecting the timeframe
  column_name <- paste0("avg_posts_per_", timeframe)
  
  # Calculation of the number of posts per day for each HEI
  posts_per_timeframe <- number_posts %>%
    left_join(date_range, by = "id") %>%
    mutate(!!column_name := round((posts / num_days), 2))
  
  print(posts_per_timeframe)
  return(posts_per_timeframe)
}
posts_per_day <- average_posts("days")
posts_per_week <- average_posts("weeks")

Plot for the average number of posts per day for each HEI

barplot(posts_per_day$avg_posts_per_days,
        names.arg = posts_per_day$id,
        main = "Average Posts per Day",
        xlab = "HEI",
        ylab = "Average Number of Posts",
        ylim = c(0, max(posts_per_day$avg_posts_per_days) + 1),
        las = 2,
        col = "#3498DB")

# Adding text labels over each bar and aligning it with the center of each bar 
text(x = barplot(posts_per_day$avg_posts_per_days, plot = FALSE),
     y = posts_per_day$avg_posts_per_days,
     labels = round(posts_per_day$avg_posts_per_days, 2),
     pos = 3)

Plot for the average number of posts per week for each HEI

barplot(posts_per_week$avg_posts_per_weeks,
        names.arg = posts_per_week$id,
        main = "Average Posts per Week",
        xlab = "HEI",
        ylab = "Average Number of Posts",
        ylim = c(0, max(posts_per_week$avg_posts_per_weeks) + 5),
        las = 2,
        col = "#E74C3C")

text(x = barplot(posts_per_week$avg_posts_per_weeks, plot = FALSE),
     y = posts_per_week$avg_posts_per_weeks,
     labels = round(posts_per_week$avg_posts_per_weeks, 2),
     pos = 3)

Defining the intervals of time for the academic year

intervals <- list(
  interval1 = as.POSIXct(c("2022-08-31", "2022-12-15")),
  interval2 = as.POSIXct(c("2023-01-04", "2023-04-01")),
  interval3 = as.POSIXct(c("2023-04-14", "2023-06-15"))
)

Function to check if a date falls within a given interval of time and apply appropriate Boolean

check_interval <- function(date) {
  for (i in 1:length(intervals)) {
    interval_start <- intervals[[i]][1]
    interval_end <- intervals[[i]][2]
    if (date >= interval_start & date <= interval_end) {
      return(TRUE)
    }
  }
  return(FALSE)
}
data_posts$academic_year <- sapply(data_posts$created_at, check_interval)
print(data.frame(id = data_posts$id, academic_year = data_posts$academic_year))

Function to count number of posts and average per day during academic time and vacation time

analyze_posts <- function(academic_year_filter) {
  # Filtering the data based on the academic_year_filter
  filtered_data <- data_posts %>%
    filter(academic_year == academic_year_filter)
  
  # Count of days for each HEI
  unique_days <- filtered_data %>%
    group_by(id) %>%
    summarise(unique_days = n_distinct(as.Date(created_at)))
  
  # Count of posts for each HEI
  number_posts_boolean <- filtered_data %>%
    group_by(id) %>%
    summarise(count = n())
  
  # Naming the column respecting the time period
  time <- ifelse(academic_year_filter, "academic_time", "vacation_time")
  column_name <- paste0("avg_posts_in_", time)
  
  # Combination of data and calculation of average posts per day
  combined_data <- left_join(unique_days, number_posts_boolean, by = "id")
  combined_data <- combined_data %>%
    mutate(!!column_name := round((count / unique_days), 2))
  
  print(combined_data)
  return(combined_data)
}
data_posts_academic <- analyze_posts(TRUE)
data_posts_vacations <- analyze_posts(FALSE)

Plot for the average number of posts during academic time for each HEI

barplot(data_posts_academic$avg_posts_in_academic_time,
        names.arg = data_posts_academic$id,
        main = "Average Posts during Academic Time",
        xlab = "HEI",
        ylab = "Average Number of Posts",
        ylim = c(0, max(data_posts_academic$avg_posts_in_academic_time) + 5),
        las = 2,
        col = "#34495E")

text(x = barplot(data_posts_academic$avg_posts_in_academic_time, plot = FALSE),
     y = data_posts_academic$avg_posts_in_academic_time,
     labels = round(data_posts_academic$avg_posts_in_academic_time, 2),
     pos = 3)

Plot for the average number of posts during vacation time for each HEI

barplot(data_posts_vacations$avg_posts_in_vacation_time,
        names.arg = data_posts_vacations$id,
        main = "Average Posts during Vacation Time",
        xlab = "HEI",
        ylab = "Average Number of Posts",
        ylim = c(0, max(data_posts_vacations$avg_posts_in_vacation_time) + 5),
        las = 2,
        col = "#D35400")

text(x = barplot(data_posts_vacations$avg_posts_in_vacation_time, plot = FALSE),
     y = data_posts_vacations$avg_posts_in_vacation_time,
     labels = round(data_posts_vacations$avg_posts_in_vacation_time, 2),
     pos = 3)

Data preparation for dates

# Creating new table that contains a new column for the day of the week
data_posts_days <- data_posts %>%
  mutate(day_of_week = weekdays(created_at))

# Selecting only the id, created_at, and day_of_week columns for the new table
data_posts_days <- data_posts_days %>%
  select(id, created_at, day_of_week)

# Create column hour from created_at
data_posts_days$created_hour <- as.numeric(format(data_posts_days$created_at, "%H"))

print(data_posts_days)
# Grouping by id and day_of_week, then counting the number of posts
number_posts_days <- data_posts_days %>%
  group_by(id, day_of_week) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
# Grouping by id, day_of_week and day created at, then counting th enumber of tweets
number_posts_per_day <- data_posts_days %>%
    mutate(created_date = as.Date(created_at)) %>% 
    group_by(id, day_of_week, created_date) %>%
    summarize(count = n())
`summarise()` has grouped output by 'id', 'day_of_week'. You can override using the `.groups` argument.
# Finding for each HEI the average count of posts per day
average_number_posts_per_day <- number_posts_per_day %>%
  group_by(id, day_of_week) %>%
  summarise(average_count = round(mean(count), 2))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(number_posts_days)

Highest and lowest posts

# Finding the HEI with the lowest count of posts per day
lowest_count <- number_posts_days %>%
  group_by(day_of_week) %>%
  slice_min(order_by = count) %>%
  select(day_of_week, id, count)

# Finding the HEI with the highest count of posts per day
highest_count <- number_posts_days %>%
  group_by(day_of_week) %>%
  slice_max(order_by = count) %>%
  select(day_of_week, id, count)

# Combine the results
high_low_HEI <- bind_rows(lowest_count, highest_count) %>%
  arrange(day_of_week)

print(high_low_HEI)

Plot for the highest and lowest count of posts per day for each day of the week

ggplot(high_low_HEI, aes(x = day_of_week, y = count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = count),
            position = position_dodge(width = 0.9),
            vjust = -0.5,
            size = 3) +
  labs(title = "Highest and Lowest Count of Posts per Day for Each Day of the Week",
       x = "Day of the Week", y = "Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Average of posts

# Finding the HEI with lowest and highest averaged count of posts per day
high_low_average_HEIs <- average_number_posts_per_day %>%
  group_by(day_of_week) %>%
  filter(average_count == max(average_count) | average_count == min(average_count)) %>%
  arrange(day_of_week, ifelse(average_count == min(average_count), average_count, -average_count))

print(high_low_average_HEIs)

Plot for the highest and lowest average count of posts per day for each day of the week

ggplot(high_low_average_HEIs, aes(x = day_of_week, y = average_count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = round(average_count, 2)),
            position = position_dodge(width = 0.7),
            vjust = -0.5,
            size = 3) +
  labs(title = "Highest and Lowest Average Count of Posts per Day for Each Day of the Week",
       x = "Day of the Week", y = "Average Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Favourite hour and day

favourite_day_hei <- number_posts_days %>%
  group_by(id) %>%
  top_n(1, count) %>%
  arrange(id)

print(favourite_day_hei)
number_posts_hours <- data_posts_days %>%
  group_by(id, created_hour) %>%
  summarise(count = n()) %>%
  ungroup()
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
favourite_hour_hei <- number_posts_hours %>%
  group_by(id) %>%
  top_n(1, count) %>%
  arrange(id)

print(favourite_hour_hei)

Heatmaps

Function to plot heatmap for various HEIs

heatmap_maker <- function(target_id){
  # Filtering data for the specific HEI
  target_data <- data_posts_days %>%
    filter(id == target_id)
  
  # Grouping by day of the week and hour, and counting the number of tweets
  tweet_counts <- target_data %>%
    group_by(day_of_week, created_hour) %>%
    summarise(num_posts = n())
  
  # Plotting heatmap
  ggplot(tweet_counts, aes(x = day_of_week, y = created_hour, fill = num_posts)) +
    geom_tile() +
    scale_fill_gradient(low = "white", high = "blue") +
    labs(title = paste("Post Heatmap for", target_id),
         x = "Day of the week",
         y = "Hour of the day")
}

Plot of heatmap for each HEI

heatmap_maker("duke.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("epfl.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("goe.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("harvard.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("leicester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("manchester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("mit.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("sb.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("stanford.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("trinity.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("wv.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("yale.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

Hashtags

# Transforming empty strings into NA
data_posts$hashtags[data_posts$hashtags == ""] <- NA

# Table with number of unique hashtags and percentage of usage
hashtags <- data_posts %>%
                group_by(id) %>%
                summarise(count = n(),
                          na = sum(is.na(hashtags)),
                          unique_hashtags = length(unique(hashtags)),
                          hashtag_percentage = round(((count - na) / count * 100), 2))

print(hashtags)

Plot for the count of unique hashtags for each HEI

barplot(hashtags$unique_hashtags,
        names.arg = hashtags$id,
        main = "Unique Hashtags for Each HEI",
        xlab = "HEI",
        ylab = "Count of Unique Hashtags",
        ylim = c(0, max(hashtags$unique_hashtags) + 50),
        las = 2,
        col= "#16A085")

text(x = barplot(hashtags$unique_hashtags, plot = FALSE),
     y = hashtags$unique_hashtags,
     labels = round(hashtags$unique_hashtags, 2),
     pos = 3)

Plot for the usage of hashtag for each HEI

barplot(hashtags$hashtag_percentage,
        names.arg = hashtags$id,
        main = "Hashtags Percentage for Each HEI",
        xlab = "HEI",
        ylab = "Hashtags Percentage",
        ylim = c(0, max(hashtags$hashtag_percentage) + 30),
        las = 2,
        col= "#F1C40F")

text(x = barplot(hashtags$hashtag_percentage, plot = FALSE),
     y = hashtags$hashtag_percentage,
     labels = round(hashtags$hashtag_percentage, 2),
     pos = 3)

URL usage

# Transforming empty strings into NA
data_posts$urls[data_posts$urls == ""] <- NA

# Table with number of post, number of NA and url percentage of usage
url_usage <- data_posts %>%
                group_by(id) %>%
                summarise(count = n(),
                          na = sum(is.na(urls)),
                          url_percentage = round(((count - na) / count * 100), 2))

print(url_usage)

Plot for the usage of hashtag for each HEI

barplot(url_usage$url_percentage,
        names.arg = url_usage$id,
        main = "Urls Percentage for Each HEI",
        xlab = "HEI",
        ylab = "Urls Percentage",
        ylim = c(0, max(url_usage$url_percentage) + 10),
        las = 2,
        col= "#8E44AD")

text(x = barplot(url_usage$url_percentage, plot = FALSE),
     y = url_usage$url_percentage,
     labels = round(url_usage$url_percentage, 2),
     pos = 3)

Text

data_posts_content <- data_posts %>%
            select(id, text)

# Counting number of words
data_posts_content <- data_posts_content %>%
  mutate(num_words = lengths(strsplit(text, "\\s+")))

# Grouping by HEI and calculate average, minimum, and maximum values of number of words
data_posts_content_metrics <- data_posts_content %>%
  group_by(id) %>%
  summarise(average_num_words = mean(num_words),
            min_num_words = min(num_words),
            max_num_words = max(num_words))
print(data_posts_content_metrics)

Plot for the average, maximum and minimum values of words for each HEI

ggplot(data_posts_content_metrics, aes(x = id, y = average_num_words)) +
  geom_point(aes(color = "Average")) +
  geom_errorbar(aes(ymin = min_num_words, ymax = max_num_words, color = "Range"), width = 0.2) +
  scale_color_manual(values = c("Average" = "#1976D2", "Range" = "#EF5350")) +
  labs(title = "Word Count Summary by HEI",
       x = "HEI",
       y = "Number of Words",
       color = "Metric") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Clusters

# Creating table for cluster algorithms

# Joining attribute percentage_tweets (percentage of tweets out of all posts) and percentage_replies (percentage of replies out of all posts) from number_posts also adding unique_hashtags (number of unique hashtags) and hashtag_percentage (percentage of posts that contain a hashtag) from hashtags, per HEI
cluster_table <- merge(select(hashtags, id, unique_hashtags, hashtag_percentage), select(data_ratio, id, percentage_tweets, percentage_replies), by = "id", all=TRUE)

# Joining attribute avg_posts_per_days (average of posts per day) from posts_per_day per HEI
cluster_table <- merge(cluster_table, select(posts_per_day, id, avg_posts_per_days), by = "id", all=TRUE)

# Joining attribute avg_posts_per_weeks (average of posts per week) from posts_per_week per HEI
cluster_table <- merge(cluster_table, select(posts_per_week, id, avg_posts_per_weeks), by = "id", all=TRUE)

# Joining attribute avg_posts_in_academic_time (average of posts during academic time)  from data_posts_academic per HEI
cluster_table <- merge(cluster_table, select(data_posts_academic, id, avg_posts_in_academic_time), by = "id", all=TRUE)

# Joining attribute avg_posts_in_vacation_time (average of posts during vacation time) from data_posts_vacations per HEI
cluster_table <- merge(cluster_table, select(data_posts_vacations, id, avg_posts_in_vacation_time), by = "id", all=TRUE)

# Joining attribute created_hour (hour where every HEI made more posts) from favourite_hour_hei per HEI
cluster_table <- merge(cluster_table, select(favourite_hour_hei, id, created_hour), by = "id", all=TRUE)

# Joining attribute url_percentage (percentage of posts that contain an url) from url_usage per HEI
cluster_table <- merge(cluster_table, select(url_usage, id, url_percentage), by = "id", all=TRUE)

# Joining attribute average_num_words (average number of words in the posts) from data_posts_content_metrics per HEI
cluster_table <- merge(cluster_table, select(data_posts_content_metrics, id, average_num_words), by = "id", all=TRUE)

print(cluster_table)

Function for cluster method

cluster_maker <- function(num_clusters, table){
  # Excluding id column for clustering
  cluster_data <- select(table, -id)
    
  # Scaling the data for kmeans method
  scaled_data <- scale(cluster_data)
  
  kmeans_model <- kmeans(scaled_data, centers = num_clusters, nstart = 10)

  # Extract cluster assignments
  cluster_assignments <- kmeans_model$cluster
  
  # Create a data frame combining original data with cluster assignments
  clustered_data <- cbind(cluster_table$id, cluster_data, cluster = cluster_assignments)
  
  clustered_data <- clustered_data[, c("cluster_table$id", "cluster")]
  
  print(clustered_data)
}

Function to discover best number of clusters

elbow_maker <- function(table){
  cluster_data <- select(table, -id)
  scaled_data <- scale(cluster_data)
  
  wss <- vector()
  range <- 1:10
  
  for (k in range) {
    kmeans_model <- kmeans(scaled_data, centers = k, nstart = 10)
    wss[k] <- kmeans_model$tot.withinss
  }
  
  elbow_df <- data.frame(k = range, WSS = wss)
  ggplot(elbow_df, aes(x = k, y = WSS)) +
    geom_line() +
    geom_point() +
    labs(x = "Number of Clusters", y = "Within-Cluster Sum of Squares (WCSS)",
         title = "Elbow Method for Optimal k") +
    theme_minimal()
}

Plot of Elbow Method and selection of best number of cluster to view how HEIs are grouped

elbow_maker(cluster_table)

cluster_maker(3, cluster_table)

Posts Classification

cleanup <- function(docs, spec.words=NULL){
  # lowercase
  docs <- tm_map(docs, content_transformer(tolower))
  # rm numbers
  docs <- tm_map(docs, removeNumbers)
  # rm english common stopWords
  docs <- tm_map(docs, removeWords, stopwords("english"))
  # if stopwords are specified as a character vector
  if(!is.null(spec.words))
    docs <- tm_map(docs, removeWords, spec.words)
  # rm punctuations
  docs <- tm_map(docs, removePunctuation)
  # rm extra white spaces
  docs <- tm_map(docs, stripWhitespace)
  # lemmatizing text
  docs <- tm_map(docs, lemmatize_words)
  
  docs
}
pairing <- function(word_dict, paired_words) {
  if (nrow(word_dict) != length(paired_words)) {
    stop("The number of rows in word_dict and the length of paired_words should be equal.")
  }
  
  word_dict <- tibble::tibble(word = word_dict$value)
  result <- tibble::tibble(word = word_dict$word, category = paired_words)
  return(result)
}
classify_text <- function(text, word_pair) {
  # Tokenizing the text and converting to lowercase
  words <- tolower(unlist(strsplit(text, "\\W+")))
  
  # Finding the frequent words in the text
  freq_words <- words[words %in% word_pair$word]
  
  if (length(freq_words) == 0) {
    return("Unknown")  # Returning Unknown if no frequent words are found
  }
  
  # Getting the corresponding categories
  categories <- word_pair$category[word_pair$word %in% freq_words]
  
  # Sorting categories by frequency in descending order and return the most frequent one
  return(names(sort(table(categories), decreasing = TRUE))[1])
}
corpus_maker <- function(text) {
  texts <- text$text
  vc <- VectorSource(texts)
  corpus <- Corpus(vc)
  
  return(corpus)
}
freq_terms <- function(clean_text, number) {
  dtm <- DocumentTermMatrix(clean_text)
  dtm.tfidf <- weightTfIdf(dtm)
  
  mdf <- as_tibble(as.matrix(dtm.tfidf))
  
  mdf.freq <- mdf %>%
  select(findFreqTerms(dtm, number)) %>%
  summarise_all(sum) %>%
  gather() %>%
  arrange(desc(value))

  mdf.freq$key <- 
    factor(mdf.freq$key,
           levels = mdf.freq$key[order(mdf.freq$value)])
  
  word_dictionary <- as_tibble(mdf.freq$key)
  
  ggplot(mdf.freq, aes(x=key, y=value)) +
    geom_bar(stat="identity") +
    labs(x="terms", y="freq") + coord_flip()
  
  return(word_dictionary)
}

Duke

duke_text <- subset(data_posts_content, id == "duke.csv")
duke_corpus <- corpus_maker(duke_text)

duke_clean_text <-cleanup(duke_corpus, c("new", "will", "change", "north", "can", "first", "year", "carolina", "years", "summer", "meet", "one"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
duke_word_dictionary <- freq_terms(duke_clean_text, 30)

duke_word_category <- c("Image", "Education", "Education", "Image", "Image", "Research", "Research", "Image", "Image", "Engagement", "Research", "Research", "Image", "Education", "Education", "Society", "Education", "Engagement")

duke_word_pair <- pairing(duke_word_dictionary, duke_word_category)

print(duke_word_pair)

# Applying the classify_text function to each text
duke_text <- duke_text %>%
  mutate(category = sapply(text, classify_text, word_pair = duke_word_pair))

print(duke_text)

epfl

epfl_text <- subset(data_posts_content, id == "epfl.csv")
epfl_corpus <- corpus_maker(epfl_text)

epfl_clean_text <- cleanup(epfl_corpus, c("new", "amp", "can", "will", "one", "now", "–", "via", "portrait"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
epfl_word_dictionary <- freq_terms(epfl_clean_text, 30)

epfl_word_category <- c("Image", "Research", "Image", "Research", "Research", "Research", "Education", "Image", "Research", "Research", "Education", "Research", NA, "Education", "Education")

epfl_word_pair <- pairing(epfl_word_dictionary, epfl_word_category)

print(epfl_word_pair)

# Applying the classify_text function to each text
epfl_text <- epfl_text %>%
  mutate(category = sapply(text, classify_text, word_pair = epfl_word_pair))

print(epfl_text)

goe

goe_text <- subset(data_posts_content, id == "goe.csv")
goe_corpus <- corpus_maker(goe_text)

goe_clean_text <- cleanup(goe_corpus, c("can", "new", "amp", "will"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
goe_word_dictionary <- freq_terms(goe_clean_text, 15)

goe_word_category <- c("Research", "Research", "Image", "Education", "Engagement", "Image", "Engagement", "Education")

goe_word_pair <- pairing(goe_word_dictionary, goe_word_category)

print(goe_word_pair)

# Applying the classify_text function to each text
goe_text <- goe_text %>%
  mutate(category = sapply(text, classify_text, word_pair = goe_word_pair))

print(goe_text)

harvard

harvard_text <- subset(data_posts_content, id == "harvard.csv")
harvard_corpus <- corpus_maker(harvard_text)

harvard_clean_text <- cleanup(harvard_corpus, c("new", "can", "will", "summer", "year", "first", "may", "-", "recent", "years", "one", "said", "time", "many", "world", "change"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
harvard_word_dictionary <- freq_terms(harvard_clean_text, 50)

harvard_word_category <- c("Image", "Education", "Research", "Image", "Research", "Research", "Engagement", "Education", "Society", "Education", "Society", "Research", "Education", "Image", "Research", NA, "Research", "Education", "Education", NA, "Research", "Society", "Society", "Research", "Education")

harvard_word_pair <- pairing(harvard_word_dictionary, harvard_word_category)

print(harvard_word_pair)

harvard_text <- harvard_text %>%
  mutate(category = sapply(text, classify_text, word_pair = harvard_word_pair))

print(harvard_text)

leicester

leicester_text <- subset(data_posts_content, id == "leicester.csv")
leicester_corpus <- corpus_maker(leicester_text)

leicester_clean_text <- cleanup(leicester_corpus, c("👉", "day", "new", "clear", "year", "can", "will", "time", "space", "one", "first"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
leicester_word_dictionary <- freq_terms(leicester_clean_text, 60)

leicester_word_category <- c("Image", NA, "Image", "Engagement", "Society", "Education", "Education", "Research", "Engagement", "Engagement", "Image", "Engagement", "Education", "Engamement", "Engagement", "Education", "Image", "Engagement", "Education", NA, "Image")

leicester_word_pair <- pairing(leicester_word_dictionary, leicester_word_category)

print(leicester_word_pair)

leicester_text <- leicester_text %>%
  mutate(category = sapply(text, classify_text, word_pair = leicester_word_pair))

print(leicester_text)

manchester

manchester_text <- subset(data_posts_content, id == "manchester.csv")
manchester_corpus <- corpus_maker(manchester_text)

manchester_clean_text <- cleanup(manchester_corpus, c("👇", "can", "just", "will", "get", "well", "one", "help", "now", "new", "read", "congratulations"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
manchester_word_dictionary <- freq_terms(manchester_clean_text, 50)

manchester_word_category <- c(NA, "Image", "Engagement", "Image", "Education", "Education", "Engagement", "Image", "Education", "Society", "Research", "Research", "Education", "Education", "Education", "Engagement", "Research", "Education", "Research")

manchester_word_pair <- pairing(manchester_word_dictionary, manchester_word_category)

print(manchester_word_pair)

manchester_text <- manchester_text %>%
  mutate(category = sapply(text, classify_text, word_pair = manchester_word_pair))

print(manchester_text)

mit

mit_text <- subset(data_posts_content, id == "mit.csv")
mit_corpus <- corpus_maker(mit_text)

mit_clean_text <- cleanup(mit_corpus, c("new", "can", "says", "“", "’", "—", "", "may", "first", "will", "using", "way", "one", "science"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
mit_word_dictionary <- freq_terms(mit_clean_text, 40)

mit_word_category <- c("Image", "Research", NA, "Image", NA, "Education", "Research", NA, "Education", "Research", "Research", "Education", "Research", "Research", "Image", "Education", "Education", "Research", "Research", "Research", "Research", "Research", "Research", "Research", "Research", "Society")

mit_word_pair <- pairing(mit_word_dictionary, mit_word_category)

print(mit_word_pair)

mit_text <- mit_text %>%
  mutate(category = sapply(text, classify_text, word_pair = mit_word_pair))

print(mit_text)

sb

sb_text <- subset(data_posts_content, id == "sb.csv")
sb_corpus <- corpus_maker(sb_text)

sb_clean_text <- cleanup(sb_corpus, c("new", "—", "will", "week", "can", "amp", "via", "now", "future", "first"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
sb_word_dictionary <- freq_terms(sb_clean_text, 30)

sb_word_category <- c("Image", "Research", "Image", "Image", "Image", NA, "Research", "Image", "Image", "Research", "Education", "Image", "Education", "Image", "Education", "Image", "Society")

sb_word_pair <- pairing(sb_word_dictionary, sb_word_category)

print(sb_word_pair)

sb_text <- sb_text %>%
  mutate(category = sapply(text, classify_text, word_pair = sb_word_pair))

print(sb_text)

stanford

stanford_text <- subset(data_posts_content, id == "stanford.csv")
stanford_corpus <- corpus_maker(stanford_text)

stanford_clean_text <- cleanup(stanford_corpus, c("new", "will"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
stanford_word_dictionary <- freq_terms(stanford_clean_text, 25)

stanford_word_category <- c("Image", "Image", "Research", "Education", NA)

stanford_word_pair <- pairing(stanford_word_dictionary, stanford_word_category)

print(stanford_word_pair)

stanford_text <- stanford_text %>%
  mutate(category = sapply(text, classify_text, word_pair = stanford_word_pair))

print(stanford_text)

trinity

trinity_text <- subset(data_posts_content, id == "trinity.csv")
trinity_corpus <- corpus_maker(trinity_text)

trinity_clean_text <- cleanup(trinity_corpus, c("amp", "read", "new", "can", "will", "week", "work", "great", "day", "visit", "irish", "first", "led", "congratulations"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
trinity_word_dictionary <- freq_terms(trinity_clean_text, 40)
Warning: empty document(s): 754
trinity_word_category <- c("Image", "Education", "Research", "Image", "Research", "Image", "Research", "Society", "Society", "Education", "Engagement", "Engagement", "Education", "Image", "Education", "Image", "Research")

trinity_word_pair <- pairing(trinity_word_dictionary, trinity_word_category)

print(trinity_word_pair)

trinity_text <- trinity_text %>%
  mutate(category = sapply(text, classify_text, word_pair = trinity_word_pair))

print(trinity_text)

wv

wv_text <- subset(data_posts_content, id == "wv.csv")
wv_corpus <- corpus_maker(wv_text)

wv_clean_text <- cleanup(wv_corpus, c("💛💙", "🙌", "👉", "happy", "day", "see", "week", "great", "can", "will", "well", "know", "new", "now", "get", "just"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
wv_word_dictionary <- freq_terms(wv_clean_text, 40)

wv_word_category <- c(NA, "Image", "Image", NA, "Image", "Image", "Engagement", "Education", "Education", "Education", NA)

wv_word_pair <- pairing(wv_word_dictionary, wv_word_category)

print(wv_word_pair)

wv_text <- wv_text %>%
  mutate(category = sapply(text, classify_text, word_pair = wv_word_pair))

print(wv_text)

yale

yale_text <- subset(data_posts_content, id == "yale.csv")
yale_corpus <- corpus_maker(yale_text)

yale_clean_text <- cleanup(yale_corpus, c("new", "—", "will", "can", "'", "first", "work", "read", "help", "year"))
Warning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documentsWarning: transformation drops documents
yale_word_dictionary <- freq_terms(yale_clean_text, 70)

yale_word_category <- c("Research", "Education", "Education", NA, "Research", "Research", "Research", "Research", "Education", "Image", "Research", "Education", "Image", "Research", NA, "Research", "Image", "Image", "Research", "Image", "Research", "Society", "Research", "Society", "Society")

yale_word_pair <- pairing(yale_word_dictionary, yale_word_category)

print(yale_word_pair)

yale_text <- yale_text %>%
  mutate(category = sapply(text, classify_text, word_pair = yale_word_pair))

print(yale_text)

Sentiment Analysis

Function for emotions

emotions_maker <- function(texts, hei_name){
  text_emotion <- get_nrc_sentiment(texts)
  
  # Proportions for text values 
  emotion_proportions <- colSums(prop.table(text_emotion[, 1:8]))
  
  # Visualization of percentages on each emotion found on posts
  barplot <- barplot(
    sort(colSums(prop.table(text_emotion[, 1:8]))), 
    horiz = TRUE, 
    cex.names = 0.7, 
    las = 1, 
    main = sprintf("Emotions found in %s's texts", hei_name), 
    xlab="Percentage",
    xlim = c(0, max(emotion_proportions) * 1.2)
  )
  
  text(
    x = sort(emotion_proportions),
    y = barplot,                    
    labels = sprintf("%.2f%%", 100 * sort(emotion_proportions)),
    pos = 4,                               
    cex = 0.7                              
  )
}

Duke

duke_text$sentiment <- round(get_sentiment(duke_text$text), 2)

print(duke_text$sentiment)
  [1]  0.00  1.00  0.85 -0.50  0.75  0.50  1.50  2.65  0.75  1.75 -1.00  1.30  1.45  1.50  1.60  2.00  0.35  2.45  1.85  0.50  1.45  0.90  1.05  1.65 -0.75
 [26] -0.50  1.00 -0.80  2.80  1.50  3.20  2.30  1.00  4.30 -0.75  2.15  0.50  2.75 -0.20  0.50  0.60  1.90  1.20 -0.75  1.25  0.10  1.95  1.95  0.00  2.10
 [51]  0.60 -1.00  0.60 -0.25  1.00  1.15  2.95  2.10  1.30  1.05  0.00  0.25  0.80  0.25  0.25  0.00  1.35  1.80  1.60  0.60  1.90  0.00  3.20  1.80  0.00
 [76]  0.40  2.05  1.40  1.60  0.20  0.40 -0.40  1.00  2.60  1.65  0.50  0.40  0.75  2.45  0.00  2.45  2.25  0.75  1.10  1.30  0.85  1.95 -1.60  1.10  1.40
[101]  2.05  1.70  3.05  1.45  3.20  0.00  2.15  0.60  0.75  2.75  1.35  4.60  1.70  2.25  1.60  0.00  0.00  1.55  0.00  2.10  4.50  3.05  1.10  2.50  1.00
[126]  0.50  0.75  0.15  1.90  4.60 -0.25  1.15  1.10  1.60  1.10  1.55  0.25  2.70  0.40  0.40  1.20 -0.85  0.80  0.35  0.35  0.00 -1.00  3.30  2.25  2.05
[151]  1.55 -0.60  3.10  0.65 -0.15  1.80  0.25  1.45  2.60  1.95  2.90  2.95  0.50  2.30  1.75 -0.50  2.40  1.00  1.20  2.50  3.05  1.75  1.75  2.00  0.80
[176]  2.80  0.40  1.35  0.35  1.65  3.00  1.35  0.00  1.10  0.60  1.95  2.65  1.40 -1.50  0.25  2.10  0.00  1.70  0.05  2.40  0.50  0.85  0.10  2.10  1.20
[201]  0.40  0.85  2.05  0.35  1.50  1.80  0.60  2.90  0.25  1.10  0.50  1.20  0.50  1.65 -0.70  0.00  2.30  2.30  0.00  0.40  0.70  0.15 -0.50  3.45  1.30
[226]  0.25  3.55  0.10  2.05  1.05  2.15  0.10  0.80  0.00  0.30  0.75  0.90  3.95  0.60 -0.15  1.40  1.80  2.30  0.00  1.60  2.05  0.90  2.35  2.00 -0.50
[251]  0.30  0.00  0.60  0.50 -0.75  0.75  0.30  2.60 -0.10  0.60  3.05  3.60  2.10  1.00  1.40  3.30  2.10  0.75  1.75  2.10  1.50  3.80  3.80  0.40  3.20
[276]  2.40  1.00  0.65  0.75  0.00  0.75  1.55  0.25  0.85  2.55  1.45  0.10 -0.30  0.10  3.05  5.25  1.55  0.15  0.50 -0.50  1.60  2.80 -2.90 -0.25  2.60
[301]  3.15 -0.50  0.40  1.60 -0.25  0.00  0.50  0.55  1.80  2.70  0.00  1.20 -0.50  0.75  1.50  2.50  0.80  0.75  1.80 -0.75  1.35  0.50  0.00  0.25  3.80
[326]  0.50  1.00  2.95  1.40  1.45  1.60  0.75  0.85  3.05  1.30  2.90  0.60  0.10  0.10  1.60 -0.25 -1.75 -1.00  1.55  0.45  0.00  1.10  0.75  0.40  0.00
[351]  0.05  1.30  1.30  0.25 -1.35  0.85  0.00  2.25  0.05  3.15  0.40  0.70  3.40  0.80  3.10  0.25  3.70  0.15  0.25  0.60  0.00 -1.65  0.50  0.65  0.40
[376]  0.80  2.40  0.80 -0.05  0.00  2.35  2.15  0.80  1.80  0.25  2.70  1.05 -1.10  0.50  4.00  0.25  0.80  0.50  0.75  2.15  0.80  0.05  3.55  1.70  0.85
[401]  0.50  1.00 -0.15  0.70  0.00  4.45  0.00 -0.60  3.95  1.15  1.25  4.10  3.00  0.35  3.45  1.60  1.30  2.15  0.80 -0.25  0.50  1.15  0.35 -1.25  1.55
[426]  2.00  1.60 -0.75  2.35  0.85  3.90  0.60  1.10  1.25  1.40 -0.10  4.10  1.30  1.65  1.45 -1.20  1.10  1.45  2.45  0.70  0.80 -2.30  3.65  2.65  1.25
[451]  0.55  3.30  0.95  0.80  2.25  5.15  0.25  1.50  1.90  2.05  0.00  4.10 -2.00  0.50  1.60  0.75  1.60  0.40  2.05  1.55  1.15  0.85  0.80  1.40  3.10
[476] -1.10 -0.25  1.45  2.00  2.05  0.40  4.50 -0.05  1.20  3.20  0.20  4.50  2.50  3.00  1.05  1.30  1.80  0.40  2.10  1.75 -0.50  1.55  3.85  1.20  2.50
[501]  1.90 -0.70  1.50  3.15  1.20  2.10  1.80  1.35 -1.05 -1.00  0.90 -2.05 -0.30  0.00  0.80  1.30  0.20  2.10  0.80 -0.10  2.20  1.55 -1.80  0.80  1.75
[526]  1.35  1.80 -2.10  1.45  0.00  0.75  0.25  0.30  1.95  0.00  0.50  3.75  1.15  1.95  1.00 -0.85  1.05  1.05  0.25  3.50  0.90  2.00 -0.50  1.00  0.55
[551]  1.00  0.60  1.55  0.45  0.45  2.65  0.80  0.40  0.25  0.00  0.25 -0.55  1.85  1.25  0.25  0.80  0.25  0.00  0.95  1.60  2.35  2.35  1.30  0.45  0.60
[576]  0.85  1.10  1.55  1.60  4.40  3.95  0.05  1.70  3.65  0.55 -0.35  1.25  1.30  0.00  0.50  1.05  0.80 -3.40 -0.10  1.00  1.55  2.05  2.95  0.85 -0.25
[601]  0.00  0.80  1.00  0.25 -0.50 -3.05 -0.50 -0.25  2.40 -1.10  4.55  1.25  1.40 -1.15  2.55  2.20  0.80  0.45  1.90  0.15  0.75  0.00  0.50 -0.25  2.55
[626]  0.00 -1.55  1.25 -0.75  0.60  0.30  0.00  2.05  0.45  2.95  3.20  1.45  0.00  1.55 -0.55  1.00  0.00  2.55  1.00 -1.75 -0.25  0.65  0.50 -0.35  2.85
[651] -0.80  0.50  0.50 -0.25  0.15  3.85  1.15  1.25  1.90  2.15  4.05  1.00  0.05  0.95  1.90  1.30  1.35  1.75  1.10  1.30  1.90 -1.00  0.00  1.75  1.65
[676] -1.25  2.50 -0.25  0.25  3.65  2.85  1.90  2.90  1.85  1.75  1.40  0.60  3.35  2.65  0.50
emotions_maker(duke_text$text, "duke")

epfl

epfl_text$sentiment <- round(get_sentiment(epfl_text$text), 2)

print(epfl_text$sentiment)
  [1] -1.75  0.80  2.75  1.55  1.55  3.10  1.00  3.10  1.00  1.40  0.00  0.65  2.20  0.50  1.00  1.25  0.50  2.30  2.45  1.65 -1.05  0.65  2.10  1.60  3.15
 [26]  0.35  1.40  0.80 -1.75  0.50  2.35 -1.05  0.35  0.80  0.85  0.00 -0.15 -0.50  1.85  3.00  1.30  0.80 -0.75  2.55  0.00  0.95  1.50  2.75  1.20  0.00
 [51]  0.70 -0.95  1.50  3.00  1.00  2.20  1.55  3.00 -1.60  0.40  0.90 -0.65  1.35  2.60  1.05  0.90  2.50  2.10  2.05 -0.25  4.50  3.35  1.80  1.70  2.85
 [76]  1.05 -0.25 -0.40  3.00  0.85  1.60  1.90  1.85  3.25  0.15  0.00 -0.05  0.95  1.45  2.20 -0.70  2.70  1.05  0.70  1.35  2.45  1.55  3.55  3.65  1.15
[101]  0.50  0.65  1.10  1.05 -0.35  3.05  1.80  1.00  1.60  0.85  1.20  3.80  5.50 -1.80  2.60  3.25  0.90  0.80  2.05  3.50  2.05  2.20  0.10  0.00  2.20
[126]  2.35 -0.65  1.35 -0.50  0.90  0.95  2.80  1.10  2.45  1.15  0.70  1.50 -0.20  0.50  1.65 -1.20  1.35  2.95  1.40  0.00  1.50  1.85  0.40  2.10  1.45
[151]  0.00  0.50 -1.00  1.00  1.65  1.90  1.00  0.75  2.25  0.00  0.30  1.55  2.65  1.35  0.75  0.00  1.90  0.30  2.50  1.85  0.85  0.80  1.90  3.25  1.90
[176]  2.55  0.60  0.00  0.40  2.30  2.45  1.05  1.10  3.15  1.65  1.20  0.10  1.95  3.30  0.25  3.00  2.05  2.10  0.35  2.40  0.60 -0.65  2.45  0.40  2.40
[201]  0.80  1.40  3.95  0.30  1.40  0.40  0.40  3.35  2.10  2.05  2.35  1.25  0.95  4.60  2.00  1.90  0.80  1.95  1.55  0.00  0.20  1.30  5.05  0.00  1.20
[226]  1.40  0.75  1.10  0.95  3.80  1.50  2.40  2.45  2.55  1.65  1.90 -0.50  1.50  3.45  2.65  1.55  0.75  1.45  1.55  1.90  2.75  1.20  1.80  1.95  3.55
[251]  1.05 -0.35  2.65  2.15  0.75  2.20  2.00  1.50  2.05  5.65  4.00  2.05 -0.15  2.10  0.90  3.00  0.60  0.00  1.40  3.90  3.00  2.35  0.25  2.65  1.75
[276]  2.50  3.40 -2.75  1.55  1.00  1.00  1.35  0.50  2.35  1.80  2.50  0.65  0.55  2.55  2.85  0.10  0.25 -0.40  1.55 -0.40  0.25  2.60  4.45  0.50  1.25
[301]  2.30  1.80 -1.05  1.15  1.25  1.20  2.75  0.35  2.20  1.00  0.30  0.60  1.55  0.40 -0.15  2.00  2.00  0.70  0.50  0.60  2.00  0.60  1.35  3.65  1.55
[326]  0.65 -1.75  0.85  0.60  0.10 -0.25  1.90  0.25  4.30  0.30  1.10  0.55  0.05  0.00 -1.25  0.25  1.20  0.75  1.75  1.15  0.40  1.65  1.25  1.00  1.30
[351]  4.50  2.55 -0.40  0.80  1.05  2.95  1.00  2.60  1.20  3.35  2.60  2.50  3.65  1.95  2.40  3.50  1.90  2.45  2.45  0.50  1.15  2.15  4.10  1.15  1.80
[376]  2.75  1.95  2.10  2.25  3.80 -0.25  1.90  2.20  0.00  2.80  1.50 -2.10  1.40  0.40  1.05  1.15  1.50  0.00  1.75  1.10  1.90  1.55 -1.05  0.10  2.10
[401]  3.00  3.25  2.65  3.85  0.90  2.70  1.75  0.25  1.80  1.40  1.00  0.55  0.25  1.90  1.20  0.10  0.80  0.80  3.30  0.30  1.65  2.45  0.25  2.90  0.00
[426]  2.95  0.75  1.80  2.00  1.25  1.30  2.05  1.95  2.50  0.40 -0.60  0.85  1.05  0.25  1.35  1.85  0.80  3.30  0.50  1.90  0.55  0.85  1.90  0.60  1.05
[451]  1.05  2.90 -2.00  2.25  1.25  2.00  1.00  3.60  3.05  2.10  2.80  0.55  2.00  1.80 -0.40  3.75  2.45  1.25  1.80 -0.15 -0.30 -0.75  3.20  0.00  2.40
[476]  1.00  4.05  0.05  1.35  2.05  1.15  2.90  1.45  2.45  2.50  4.90  1.85  0.50  0.10  3.05  3.35 -0.05  1.25  2.95  1.30  2.10  2.55  1.55  4.45  4.80
[501]  3.70  0.60  3.25  0.00  1.30  1.50  2.80  0.10  1.00  0.40  3.00  0.50  2.80  2.35  2.65  2.50  2.95  1.70  0.00  0.25  3.60  1.40  2.15  1.25  0.10
[526]  2.90  1.70  3.90  1.15 -0.10  1.65  0.05  3.70  3.35  1.25 -0.90  0.50  1.00  0.65  1.75  1.50  1.75  1.75  1.30  0.25  1.80  0.75  3.20 -1.65  1.80
[551] -0.25  0.00  2.20 -0.30  0.60  0.00  1.55  0.80  1.45  2.65  0.05  0.80  0.90  1.45  0.75  2.65  1.15  0.65  0.75  2.45  1.00  0.75  0.15 -1.65  1.80
[576]  0.50  1.00  1.30 -1.75  1.45  0.75  0.40  3.00  1.50  0.80  3.90 -1.55  1.60  2.30  4.45  1.00  2.75 -0.75
emotions_maker(epfl_text$text, "epfl")

goe

goe_text$sentiment <- round(get_sentiment(goe_text$text), 2)

print(goe_text)
emotions_maker(goe_text$text, "goe")

harvard

harvard_text$sentiment <- round(get_sentiment(harvard_text$text), 2)

print(harvard_text)
emotions_maker(harvard_text$text, "harvard")

leicester

leicester_text$sentiment <- round(get_sentiment(leicester_text$text), 2)

print(leicester_text)
emotions_maker(leicester_text$text, "leicester")

manchester

manchester_text$sentiment <- round(get_sentiment(manchester_text$text), 2)

print(manchester_text)
emotions_maker(manchester_text$text, "manchester")

mit

mit_text$sentiment <- round(get_sentiment(mit_text$text), 2)

print(mit_text)
emotions_maker(mit_text$text, "mit")

sb

sb_text$sentiment <- round(get_sentiment(sb_text$text), 2)

print(sb_text)
emotions_maker(sb_text$text, "sb")

stanford

stanford_text$sentiment <- round(get_sentiment(stanford_text$text), 2)

print(stanford_text)
emotions_maker(stanford_text$text, "stanford")

trinity

trinity_text$sentiment <- round(get_sentiment(trinity_text$text), 2)

print(trinity_text)
emotions_maker(trinity_text$text, "trinity")

wv

wv_text$sentiment <- round(get_sentiment(wv_text$text), 2)

print(wv_text)
emotions_maker(wv_text$text, "wv")

yale

yale_text$sentiment <- round(get_sentiment(yale_text$text), 2)

print(yale_text)
emotions_maker(yale_text$text, "yale")

LS0tCnRpdGxlOiAiUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIyMgRm9yIFIgYmVnaW5uZXJzCk5ldyBjaHVuayAqQ3RybCtBbHQrSSoKCkV4ZWN1dGUgY2h1bmsgKkN0cmwrU2hpZnQrRW50ZXIqCgpFeGVjdXRlIGFsbCBjaHVua3MgKkN0cmwrQWx0K1IqCgpIVE1MIHByZXZpZXcgKkN0cmwrU2hpZnQrSyoKCiMgTGlicmFyeSBwcmVwYXJhdGlvbnMKCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHN0YXRzKQpsaWJyYXJ5KHRtKQpsaWJyYXJ5KHRleHQydmVjKQpsaWJyYXJ5KHRleHRzdGVtKQpsaWJyYXJ5KHN5dXpoZXQpCmBgYAoKIyBEYXRhIEltcG9ydAoKYGBge3J9CmRhdGFfcG9zdHMgPC0gcmVhZC5jc3YoIn4vNHllYXIvMnNlbWVzdGVyL2R0SUkvQ1NWcy9IRUlzLmNzdiIsCiAgICAgICAgICAgICAgICAgY29sQ2xhc3NlcyA9IGModHdlZXRfaWQgPSAiY2hhcmFjdGVyIikpCgojIE1vZGlmeWluZyBjcmVhdGVkX2F0IHR5cGUgc28gdGhhdCBhdHRyaWJ1dGUgY2FuIGJlIHVzZWQgbW9yZSBlYXNpbHkgCmRhdGFfcG9zdHMkY3JlYXRlZF9hdCA8LSBhcy5QT1NJWGN0KGRhdGFfcG9zdHMkY3JlYXRlZF9hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0PSAiJVktJW0tJWRUJUg6JU06JVMiLCB0ej0iVVRDIikKCiNWaWV3KGRhdGEpCnN1bW1hcnkoZGF0YV9wb3N0cykKYGBgCgojIEluaXRpYWwgRGF0YSBQcmVwYXJhdGlvbgoKYGBge3J9CiMgQ291bnQgb2YgaG93IG1hbnkgZW50cmllcyBlYWNoIEhFSSBoYXMKbnVtYmVyX3Bvc3RzIDwtIGRhdGFfcG9zdHMgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfcG9zdHMKYGBgCgojIFNpbmNlIGNvbXBsdXRlbnNlIG9ubHkgaGFzIDEgZW50cnkgd2UgY2FuJ3QgbGVhcm4gYW55dGhpbmcgZnJvbSBpdCwgc28gd2UgcmVtb3ZlZCBpdAoKYGBge3J9CmRhdGFfcG9zdHMgPC0gZGF0YV9wb3N0c1tkYXRhX3Bvc3RzJGlkICE9ICJjb21wbHV0ZW5zZS5jc3YiLCBdCmBgYAoKIyBWaXN1YWxpemF0aW9uIG9mIGFsbCBwb3N0cywganVzdCB0d2VldHMgYW5kIGp1c3QgcmVwbGllcwoKYGBge3J9Cm51bWJlcl9wb3N0cyA8LSBkYXRhX3Bvc3RzICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKHBvc3RzID0gbigpKQoKbnVtYmVyX3R3ZWV0cyA8LSBkYXRhX3Bvc3RzW2RhdGFfcG9zdHMkdHlwZSA9PSAiVHdlZXQiLCBdICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKHR3ZWV0cyA9IG4oKSkKCm51bWJlcl9yZXBsaWVzIDwtIGRhdGFfcG9zdHNbZGF0YV9wb3N0cyR0eXBlID09ICJSZXBseSIsIF0gJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UocmVwbGllcyA9IG4oKSkKCnByaW50KG51bWJlcl9wb3N0cykKcHJpbnQobnVtYmVyX3R3ZWV0cykKcHJpbnQobnVtYmVyX3JlcGxpZXMpCmBgYAoKIyBDYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZSBvZiB0d2VldHMgYW5kIHJlcGxpZXMgYmFzZWQgb24gYWxsIHBvc3RzCgpgYGB7cn0KIyBNZXJnaW5nIHRoZSBjb3VudHMgb2YgdHdlZXRzIGFuZCByZXBsaWVzIHdpdGggdGhlIGNvdW50IG9mIHBvc3RzCmRhdGFfcmF0aW8gPC0gbWVyZ2UobnVtYmVyX3Bvc3RzLCBudW1iZXJfdHdlZXRzLCBieSA9ICJpZCIsIGFsbCA9IFRSVUUpCmRhdGFfcmF0aW8gPC0gbWVyZ2UoZGF0YV9yYXRpbywgbnVtYmVyX3JlcGxpZXMsIGJ5ID0gImlkIiwgYWxsID0gVFJVRSkKCgpkYXRhX3JhdGlvJHBlcmNlbnRhZ2VfdHdlZXRzIDwtIHJvdW5kKCgoZGF0YV9yYXRpbyR0d2VldHMgLyBkYXRhX3JhdGlvJHBvc3RzKSAqIDEwMCksIDIpCmRhdGFfcmF0aW8kcGVyY2VudGFnZV9yZXBsaWVzIDwtIHJvdW5kKCgoZGF0YV9yYXRpbyRyZXBsaWVzIC8gZGF0YV9yYXRpbyRwb3N0cykgKiAxMDApLCAyKQoKZGF0YV9yYXRpbyA8LSBkYXRhX3JhdGlvWywgYygiaWQiLCAicGVyY2VudGFnZV90d2VldHMiLCAicGVyY2VudGFnZV9yZXBsaWVzIildCgpkYXRhX3JhdGlvJHBlcmNlbnRhZ2VfcmVwbGllc1tpcy5uYShkYXRhX3JhdGlvJHBlcmNlbnRhZ2VfcmVwbGllcyldIDwtIDAKCnByaW50KGRhdGFfcmF0aW8pCmBgYAoKIyBOQSByZW1vdmFsCgojIEZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSB0aGUgbnVtYmVyIG9mIE5BcyBpbiBhbGwgY29sdW1ucwoKYGBge3J9Cm5hX2NvdW50IDwtIGZ1bmN0aW9uKCl7CiAgIyBDb3VudGluZyB0aGUgbnVtYmVyIG9mIE5BIHZhbHVlcyBmb3IgZWFjaCBjb2x1bW4KICBuYV9jb3VudCA8LSBjb2xTdW1zKGlzLm5hKGRhdGFfcG9zdHMpKQogIAogICMgQ3JlYXRpbmcgYSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSBOQSBjb3VudHMKICBuYV9jb3VudHNfdGFibGUgPC0gZGF0YS5mcmFtZShDb2x1bW4gPSBuYW1lcyhuYV9jb3VudCksIE5BX0NvdW50ID0gbmFfY291bnQpCiAgCiAgcHJpbnQobmFfY291bnRzX3RhYmxlKQp9CmBgYAoKIyBDYWxjdWxhdGlvbnMgb2YgdmlldywgZmF2b3VyaXRlLCByZXR3ZWV0IGFuZCByZXBseSBwZXJjZW50aWxlcyBhbmQgdmlzdWFsaXphdGlvbiBvZiBOQXMgaW4gYWxsIGNvbHVtbnMKCmBgYHtyfQpkYXRhX3Bvc3RzIDwtIGRhdGFfcG9zdHMgJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIG11dGF0ZSh2aWV3X3BlcmNlbnRpbGUgPSBudGlsZSh2aWV3X2NvdW50LCAxMDApLAogICAgICAgICBmYXZvcml0ZV9wZXJjZW50aWxlID0gbnRpbGUoZmF2b3JpdGVfY291bnQsIDEwMCksCiAgICAgICAgIHJldHdlZXRfcGVyY2VudGlsZSA9IG50aWxlKHJldHdlZXRfY291bnQsIDEwMCksCiAgICAgICAgIHJlcGx5X3BlcmNlbnRpbGUgPSBudGlsZShyZXBseV9jb3VudCwgMTAwKSkgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShhdmdfcGVyY2VudGlsZSA9IHJvdW5kKG1lYW4oYyh2aWV3X3BlcmNlbnRpbGUsIGZhdm9yaXRlX3BlcmNlbnRpbGUsIHJldHdlZXRfcGVyY2VudGlsZSwgcmVwbHlfcGVyY2VudGlsZSksIG5hLnJtID0gVFJVRSksIDIpKQoKbmFfY291bnQoKQoKZGF0YV9wZXJjZW50aWxlIDwtIGRhdGFfcG9zdHNbLCBjKCJpZCIsICJ2aWV3X3BlcmNlbnRpbGUiLCAiZmF2b3JpdGVfcGVyY2VudGlsZSIsICJyZXR3ZWV0X3BlcmNlbnRpbGUiLCAicmVwbHlfcGVyY2VudGlsZSIsICJhdmdfcGVyY2VudGlsZSIpXQoKcHJpbnQoZGF0YV9wZXJjZW50aWxlKQpgYGAKCiMgQ2FsY3VsYXRpb24gb2YgdGhlIG1heGltdW0gbnVtYmVyIG9mIHZpZXdzIGZvciBlYWNoIEhFSQoKYGBge3J9Cm1heF92aWV3X2NvdW50cyA8LSB0YXBwbHkoZGF0YV9wb3N0cyR2aWV3X2NvdW50LCBkYXRhX3Bvc3RzJGlkLCBtYXgsIG5hLnJtID0gVFJVRSkKCnByaW50KG1heF92aWV3X2NvdW50cykKYGBgCgojIFJlbW92YWwgb2YgTkFzCgpgYGB7cn0KIyBGcm9tIHZpZXcgY291bnQKZGF0YV9wb3N0cyR2aWV3X2NvdW50IDwtIGlmZWxzZSgKICBpcy5uYShkYXRhX3Bvc3RzJHZpZXdfY291bnQpLAogIHJvdW5kKG1heF92aWV3X2NvdW50c1tkYXRhX3Bvc3RzJGlkXSAqIChkYXRhX3Bvc3RzJGF2Z19wZXJjZW50aWxlIC8gMTAwKSksCiAgZGF0YV9wb3N0cyR2aWV3X2NvdW50KQoKIyBGcm9tIHZpZXcgcGVyY2VudGlsZQpkYXRhX3Bvc3RzJHZpZXdfcGVyY2VudGlsZSA8LSBpZmVsc2UoCiAgaXMubmEoZGF0YV9wb3N0cyR2aWV3X3BlcmNlbnRpbGUpLAogIGRhdGFfcG9zdHMkYXZnX3BlcmNlbnRpbGUsCiAgZGF0YV9wb3N0cyR2aWV3X3BlcmNlbnRpbGUpCmBgYAoKIyBWaXN1YWxpemF0aW9uIG9mIE5BcyBpbiBhbGwgY29sdW1ucwoKYGBge3J9Cm5hX2NvdW50KCkKYGBgCgojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBhdmVyYWdlIHBvc3RzCgpgYGB7cn0KYXZlcmFnZV9wb3N0cyA8LSBmdW5jdGlvbih0aW1lZnJhbWUpewogICMgQ2FsY3VsYXRpb24gb2YgdGhlIHRpbWVmcmFtZSBiZXR3ZWVuIGVhcmxpZXN0IGFuZCBsYXRlc3QgcG9zdCBmb3IgZWFjaCBIRUkKICBkYXRlX3JhbmdlIDwtIGRhdGFfcG9zdHMgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UobWluX2RhdGUgPSBtaW4oY3JlYXRlZF9hdCksCiAgICAgICAgICAgICAgbWF4X2RhdGUgPSBtYXgoY3JlYXRlZF9hdCkpICU+JQogICAgbXV0YXRlKG51bV9kYXlzID0gYXMubnVtZXJpYyhkaWZmdGltZShtYXhfZGF0ZSwgbWluX2RhdGUsIHVuaXRzID0gdGltZWZyYW1lKSkpCiAgCiAgIyBOYW1pbmcgdGhlIGNvbHVtbiByZXNwZWN0aW5nIHRoZSB0aW1lZnJhbWUKICBjb2x1bW5fbmFtZSA8LSBwYXN0ZTAoImF2Z19wb3N0c19wZXJfIiwgdGltZWZyYW1lKQogIAogICMgQ2FsY3VsYXRpb24gb2YgdGhlIG51bWJlciBvZiBwb3N0cyBwZXIgZGF5IGZvciBlYWNoIEhFSQogIHBvc3RzX3Blcl90aW1lZnJhbWUgPC0gbnVtYmVyX3Bvc3RzICU+JQogICAgbGVmdF9qb2luKGRhdGVfcmFuZ2UsIGJ5ID0gImlkIikgJT4lCiAgICBtdXRhdGUoISFjb2x1bW5fbmFtZSA6PSByb3VuZCgocG9zdHMgLyBudW1fZGF5cyksIDIpKQogIAogIHByaW50KHBvc3RzX3Blcl90aW1lZnJhbWUpCiAgcmV0dXJuKHBvc3RzX3Blcl90aW1lZnJhbWUpCn0KYGBgCgpgYGB7cn0KcG9zdHNfcGVyX2RheSA8LSBhdmVyYWdlX3Bvc3RzKCJkYXlzIikKcG9zdHNfcGVyX3dlZWsgPC0gYXZlcmFnZV9wb3N0cygid2Vla3MiKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHBvc3RzIHBlciBkYXkgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdChwb3N0c19wZXJfZGF5JGF2Z19wb3N0c19wZXJfZGF5cywKICAgICAgICBuYW1lcy5hcmcgPSBwb3N0c19wZXJfZGF5JGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBQb3N0cyBwZXIgRGF5IiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBQb3N0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHBvc3RzX3Blcl9kYXkkYXZnX3Bvc3RzX3Blcl9kYXlzKSArIDEpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiMzNDk4REIiKQoKIyBBZGRpbmcgdGV4dCBsYWJlbHMgb3ZlciBlYWNoIGJhciBhbmQgYWxpZ25pbmcgaXQgd2l0aCB0aGUgY2VudGVyIG9mIGVhY2ggYmFyIAp0ZXh0KHggPSBiYXJwbG90KHBvc3RzX3Blcl9kYXkkYXZnX3Bvc3RzX3Blcl9kYXlzLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSBwb3N0c19wZXJfZGF5JGF2Z19wb3N0c19wZXJfZGF5cywKICAgICBsYWJlbHMgPSByb3VuZChwb3N0c19wZXJfZGF5JGF2Z19wb3N0c19wZXJfZGF5cywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiBwb3N0cyBwZXIgd2VlayBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KHBvc3RzX3Blcl93ZWVrJGF2Z19wb3N0c19wZXJfd2Vla3MsCiAgICAgICAgbmFtZXMuYXJnID0gcG9zdHNfcGVyX3dlZWskaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFBvc3RzIHBlciBXZWVrIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBQb3N0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHBvc3RzX3Blcl93ZWVrJGF2Z19wb3N0c19wZXJfd2Vla3MpICsgNSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiI0U3NEMzQyIpCgp0ZXh0KHggPSBiYXJwbG90KHBvc3RzX3Blcl93ZWVrJGF2Z19wb3N0c19wZXJfd2Vla3MsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHBvc3RzX3Blcl93ZWVrJGF2Z19wb3N0c19wZXJfd2Vla3MsCiAgICAgbGFiZWxzID0gcm91bmQocG9zdHNfcGVyX3dlZWskYXZnX3Bvc3RzX3Blcl93ZWVrcywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIERlZmluaW5nIHRoZSBpbnRlcnZhbHMgb2YgdGltZSBmb3IgdGhlIGFjYWRlbWljIHllYXIKCmBgYHtyfQppbnRlcnZhbHMgPC0gbGlzdCgKICBpbnRlcnZhbDEgPSBhcy5QT1NJWGN0KGMoIjIwMjItMDgtMzEiLCAiMjAyMi0xMi0xNSIpKSwKICBpbnRlcnZhbDIgPSBhcy5QT1NJWGN0KGMoIjIwMjMtMDEtMDQiLCAiMjAyMy0wNC0wMSIpKSwKICBpbnRlcnZhbDMgPSBhcy5QT1NJWGN0KGMoIjIwMjMtMDQtMTQiLCAiMjAyMy0wNi0xNSIpKQopCmBgYAoKIyBGdW5jdGlvbiB0byBjaGVjayBpZiBhIGRhdGUgZmFsbHMgd2l0aGluIGEgZ2l2ZW4gaW50ZXJ2YWwgb2YgdGltZSBhbmQgYXBwbHkgYXBwcm9wcmlhdGUgQm9vbGVhbgoKYGBge3J9CmNoZWNrX2ludGVydmFsIDwtIGZ1bmN0aW9uKGRhdGUpIHsKICBmb3IgKGkgaW4gMTpsZW5ndGgoaW50ZXJ2YWxzKSkgewogICAgaW50ZXJ2YWxfc3RhcnQgPC0gaW50ZXJ2YWxzW1tpXV1bMV0KICAgIGludGVydmFsX2VuZCA8LSBpbnRlcnZhbHNbW2ldXVsyXQogICAgaWYgKGRhdGUgPj0gaW50ZXJ2YWxfc3RhcnQgJiBkYXRlIDw9IGludGVydmFsX2VuZCkgewogICAgICByZXR1cm4oVFJVRSkKICAgIH0KICB9CiAgcmV0dXJuKEZBTFNFKQp9CmBgYAoKYGBge3J9CmRhdGFfcG9zdHMkYWNhZGVtaWNfeWVhciA8LSBzYXBwbHkoZGF0YV9wb3N0cyRjcmVhdGVkX2F0LCBjaGVja19pbnRlcnZhbCkKcHJpbnQoZGF0YS5mcmFtZShpZCA9IGRhdGFfcG9zdHMkaWQsIGFjYWRlbWljX3llYXIgPSBkYXRhX3Bvc3RzJGFjYWRlbWljX3llYXIpKQpgYGAKCiMgRnVuY3Rpb24gdG8gY291bnQgbnVtYmVyIG9mIHBvc3RzIGFuZCBhdmVyYWdlIHBlciBkYXkgZHVyaW5nIGFjYWRlbWljIHRpbWUgYW5kIHZhY2F0aW9uIHRpbWUKCmBgYHtyfQphbmFseXplX3Bvc3RzIDwtIGZ1bmN0aW9uKGFjYWRlbWljX3llYXJfZmlsdGVyKSB7CiAgIyBGaWx0ZXJpbmcgdGhlIGRhdGEgYmFzZWQgb24gdGhlIGFjYWRlbWljX3llYXJfZmlsdGVyCiAgZmlsdGVyZWRfZGF0YSA8LSBkYXRhX3Bvc3RzICU+JQogICAgZmlsdGVyKGFjYWRlbWljX3llYXIgPT0gYWNhZGVtaWNfeWVhcl9maWx0ZXIpCiAgCiAgIyBDb3VudCBvZiBkYXlzIGZvciBlYWNoIEhFSQogIHVuaXF1ZV9kYXlzIDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UodW5pcXVlX2RheXMgPSBuX2Rpc3RpbmN0KGFzLkRhdGUoY3JlYXRlZF9hdCkpKQogIAogICMgQ291bnQgb2YgcG9zdHMgZm9yIGVhY2ggSEVJCiAgbnVtYmVyX3Bvc3RzX2Jvb2xlYW4gPC0gZmlsdGVyZWRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKICAKICAjIE5hbWluZyB0aGUgY29sdW1uIHJlc3BlY3RpbmcgdGhlIHRpbWUgcGVyaW9kCiAgdGltZSA8LSBpZmVsc2UoYWNhZGVtaWNfeWVhcl9maWx0ZXIsICJhY2FkZW1pY190aW1lIiwgInZhY2F0aW9uX3RpbWUiKQogIGNvbHVtbl9uYW1lIDwtIHBhc3RlMCgiYXZnX3Bvc3RzX2luXyIsIHRpbWUpCiAgCiAgIyBDb21iaW5hdGlvbiBvZiBkYXRhIGFuZCBjYWxjdWxhdGlvbiBvZiBhdmVyYWdlIHBvc3RzIHBlciBkYXkKICBjb21iaW5lZF9kYXRhIDwtIGxlZnRfam9pbih1bmlxdWVfZGF5cywgbnVtYmVyX3Bvc3RzX2Jvb2xlYW4sIGJ5ID0gImlkIikKICBjb21iaW5lZF9kYXRhIDwtIGNvbWJpbmVkX2RhdGEgJT4lCiAgICBtdXRhdGUoISFjb2x1bW5fbmFtZSA6PSByb3VuZCgoY291bnQgLyB1bmlxdWVfZGF5cyksIDIpKQogIAogIHByaW50KGNvbWJpbmVkX2RhdGEpCiAgcmV0dXJuKGNvbWJpbmVkX2RhdGEpCn0KYGBgCgpgYGB7cn0KZGF0YV9wb3N0c19hY2FkZW1pYyA8LSBhbmFseXplX3Bvc3RzKFRSVUUpCmRhdGFfcG9zdHNfdmFjYXRpb25zIDwtIGFuYWx5emVfcG9zdHMoRkFMU0UpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSBudW1iZXIgb2YgcG9zdHMgZHVyaW5nIGFjYWRlbWljIHRpbWUgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdChkYXRhX3Bvc3RzX2FjYWRlbWljJGF2Z19wb3N0c19pbl9hY2FkZW1pY190aW1lLAogICAgICAgIG5hbWVzLmFyZyA9IGRhdGFfcG9zdHNfYWNhZGVtaWMkaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFBvc3RzIGR1cmluZyBBY2FkZW1pYyBUaW1lIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBQb3N0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KGRhdGFfcG9zdHNfYWNhZGVtaWMkYXZnX3Bvc3RzX2luX2FjYWRlbWljX3RpbWUpICsgNSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiIzM0NDk1RSIpCgp0ZXh0KHggPSBiYXJwbG90KGRhdGFfcG9zdHNfYWNhZGVtaWMkYXZnX3Bvc3RzX2luX2FjYWRlbWljX3RpbWUsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IGRhdGFfcG9zdHNfYWNhZGVtaWMkYXZnX3Bvc3RzX2luX2FjYWRlbWljX3RpbWUsCiAgICAgbGFiZWxzID0gcm91bmQoZGF0YV9wb3N0c19hY2FkZW1pYyRhdmdfcG9zdHNfaW5fYWNhZGVtaWNfdGltZSwgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiBwb3N0cyBkdXJpbmcgdmFjYXRpb24gdGltZSBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KGRhdGFfcG9zdHNfdmFjYXRpb25zJGF2Z19wb3N0c19pbl92YWNhdGlvbl90aW1lLAogICAgICAgIG5hbWVzLmFyZyA9IGRhdGFfcG9zdHNfdmFjYXRpb25zJGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBQb3N0cyBkdXJpbmcgVmFjYXRpb24gVGltZSIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiQXZlcmFnZSBOdW1iZXIgb2YgUG9zdHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChkYXRhX3Bvc3RzX3ZhY2F0aW9ucyRhdmdfcG9zdHNfaW5fdmFjYXRpb25fdGltZSkgKyA1KSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbCA9ICIjRDM1NDAwIikKCnRleHQoeCA9IGJhcnBsb3QoZGF0YV9wb3N0c192YWNhdGlvbnMkYXZnX3Bvc3RzX2luX3ZhY2F0aW9uX3RpbWUsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IGRhdGFfcG9zdHNfdmFjYXRpb25zJGF2Z19wb3N0c19pbl92YWNhdGlvbl90aW1lLAogICAgIGxhYmVscyA9IHJvdW5kKGRhdGFfcG9zdHNfdmFjYXRpb25zJGF2Z19wb3N0c19pbl92YWNhdGlvbl90aW1lLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgRGF0YSBwcmVwYXJhdGlvbiBmb3IgZGF0ZXMgCgpgYGB7cn0KIyBDcmVhdGluZyBuZXcgdGFibGUgdGhhdCBjb250YWlucyBhIG5ldyBjb2x1bW4gZm9yIHRoZSBkYXkgb2YgdGhlIHdlZWsKZGF0YV9wb3N0c19kYXlzIDwtIGRhdGFfcG9zdHMgJT4lCiAgbXV0YXRlKGRheV9vZl93ZWVrID0gd2Vla2RheXMoY3JlYXRlZF9hdCkpCgojIFNlbGVjdGluZyBvbmx5IHRoZSBpZCwgY3JlYXRlZF9hdCwgYW5kIGRheV9vZl93ZWVrIGNvbHVtbnMgZm9yIHRoZSBuZXcgdGFibGUKZGF0YV9wb3N0c19kYXlzIDwtIGRhdGFfcG9zdHNfZGF5cyAlPiUKICBzZWxlY3QoaWQsIGNyZWF0ZWRfYXQsIGRheV9vZl93ZWVrKQoKIyBDcmVhdGUgY29sdW1uIGhvdXIgZnJvbSBjcmVhdGVkX2F0CmRhdGFfcG9zdHNfZGF5cyRjcmVhdGVkX2hvdXIgPC0gYXMubnVtZXJpYyhmb3JtYXQoZGF0YV9wb3N0c19kYXlzJGNyZWF0ZWRfYXQsICIlSCIpKQoKcHJpbnQoZGF0YV9wb3N0c19kYXlzKQpgYGAKCmBgYHtyfQojIEdyb3VwaW5nIGJ5IGlkIGFuZCBkYXlfb2Zfd2VlaywgdGhlbiBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHBvc3RzCm51bWJlcl9wb3N0c19kYXlzIDwtIGRhdGFfcG9zdHNfZGF5cyAlPiUKICBncm91cF9ieShpZCwgZGF5X29mX3dlZWspICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCiMgR3JvdXBpbmcgYnkgaWQsIGRheV9vZl93ZWVrIGFuZCBkYXkgY3JlYXRlZCBhdCwgdGhlbiBjb3VudGluZyB0aCBlbnVtYmVyIG9mIHR3ZWV0cwpudW1iZXJfcG9zdHNfcGVyX2RheSA8LSBkYXRhX3Bvc3RzX2RheXMgJT4lCiAgICBtdXRhdGUoY3JlYXRlZF9kYXRlID0gYXMuRGF0ZShjcmVhdGVkX2F0KSkgJT4lIAogICAgZ3JvdXBfYnkoaWQsIGRheV9vZl93ZWVrLCBjcmVhdGVkX2RhdGUpICU+JQogICAgc3VtbWFyaXplKGNvdW50ID0gbigpKQoKIyBGaW5kaW5nIGZvciBlYWNoIEhFSSB0aGUgYXZlcmFnZSBjb3VudCBvZiBwb3N0cyBwZXIgZGF5CmF2ZXJhZ2VfbnVtYmVyX3Bvc3RzX3Blcl9kYXkgPC0gbnVtYmVyX3Bvc3RzX3Blcl9kYXkgJT4lCiAgZ3JvdXBfYnkoaWQsIGRheV9vZl93ZWVrKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9jb3VudCA9IHJvdW5kKG1lYW4oY291bnQpLCAyKSkKCnByaW50KG51bWJlcl9wb3N0c19kYXlzKQpgYGAKCiMgSGlnaGVzdCBhbmQgbG93ZXN0IHBvc3RzCgpgYGB7cn0KIyBGaW5kaW5nIHRoZSBIRUkgd2l0aCB0aGUgbG93ZXN0IGNvdW50IG9mIHBvc3RzIHBlciBkYXkKbG93ZXN0X2NvdW50IDwtIG51bWJlcl9wb3N0c19kYXlzICU+JQogIGdyb3VwX2J5KGRheV9vZl93ZWVrKSAlPiUKICBzbGljZV9taW4ob3JkZXJfYnkgPSBjb3VudCkgJT4lCiAgc2VsZWN0KGRheV9vZl93ZWVrLCBpZCwgY291bnQpCgojIEZpbmRpbmcgdGhlIEhFSSB3aXRoIHRoZSBoaWdoZXN0IGNvdW50IG9mIHBvc3RzIHBlciBkYXkKaGlnaGVzdF9jb3VudCA8LSBudW1iZXJfcG9zdHNfZGF5cyAlPiUKICBncm91cF9ieShkYXlfb2Zfd2VlaykgJT4lCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gY291bnQpICU+JQogIHNlbGVjdChkYXlfb2Zfd2VlaywgaWQsIGNvdW50KQoKIyBDb21iaW5lIHRoZSByZXN1bHRzCmhpZ2hfbG93X0hFSSA8LSBiaW5kX3Jvd3MobG93ZXN0X2NvdW50LCBoaWdoZXN0X2NvdW50KSAlPiUKICBhcnJhbmdlKGRheV9vZl93ZWVrKQoKcHJpbnQoaGlnaF9sb3dfSEVJKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCBjb3VudCBvZiBwb3N0cyBwZXIgZGF5IGZvciBlYWNoIGRheSBvZiB0aGUgd2VlawoKYGBge3J9CmdncGxvdChoaWdoX2xvd19IRUksIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBjb3VudCwgZmlsbCA9IGlkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnQpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LAogICAgICAgICAgICBzaXplID0gMykgKwogIGxhYnModGl0bGUgPSAiSGlnaGVzdCBhbmQgTG93ZXN0IENvdW50IG9mIFBvc3RzIHBlciBEYXkgZm9yIEVhY2ggRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHggPSAiRGF5IG9mIHRoZSBXZWVrIiwgeSA9ICJDb3VudCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSByYWluYm93KGxlbmd0aCh1bmlxdWUoaGlnaF9sb3dfSEVJJGlkKSkpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyBBdmVyYWdlIG9mIHBvc3RzCgpgYGB7cn0KIyBGaW5kaW5nIHRoZSBIRUkgd2l0aCBsb3dlc3QgYW5kIGhpZ2hlc3QgYXZlcmFnZWQgY291bnQgb2YgcG9zdHMgcGVyIGRheQpoaWdoX2xvd19hdmVyYWdlX0hFSXMgPC0gYXZlcmFnZV9udW1iZXJfcG9zdHNfcGVyX2RheSAlPiUKICBncm91cF9ieShkYXlfb2Zfd2VlaykgJT4lCiAgZmlsdGVyKGF2ZXJhZ2VfY291bnQgPT0gbWF4KGF2ZXJhZ2VfY291bnQpIHwgYXZlcmFnZV9jb3VudCA9PSBtaW4oYXZlcmFnZV9jb3VudCkpICU+JQogIGFycmFuZ2UoZGF5X29mX3dlZWssIGlmZWxzZShhdmVyYWdlX2NvdW50ID09IG1pbihhdmVyYWdlX2NvdW50KSwgYXZlcmFnZV9jb3VudCwgLWF2ZXJhZ2VfY291bnQpKQoKcHJpbnQoaGlnaF9sb3dfYXZlcmFnZV9IRUlzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCBhdmVyYWdlIGNvdW50IG9mIHBvc3RzIHBlciBkYXkgZm9yIGVhY2ggZGF5IG9mIHRoZSB3ZWVrCgpgYGB7cn0KZ2dwbG90KGhpZ2hfbG93X2F2ZXJhZ2VfSEVJcywgYWVzKHggPSBkYXlfb2Zfd2VlaywgeSA9IGF2ZXJhZ2VfY291bnQsIGZpbGwgPSBpZCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGF2ZXJhZ2VfY291bnQsIDIpKSwKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNyksCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwKICAgICAgICAgICAgc2l6ZSA9IDMpICsKICBsYWJzKHRpdGxlID0gIkhpZ2hlc3QgYW5kIExvd2VzdCBBdmVyYWdlIENvdW50IG9mIFBvc3RzIHBlciBEYXkgZm9yIEVhY2ggRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHggPSAiRGF5IG9mIHRoZSBXZWVrIiwgeSA9ICJBdmVyYWdlIENvdW50IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShoaWdoX2xvd19IRUkkaWQpKSkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIEZhdm91cml0ZSBob3VyIGFuZCBkYXkKCmBgYHtyfQpmYXZvdXJpdGVfZGF5X2hlaSA8LSBudW1iZXJfcG9zdHNfZGF5cyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgdG9wX24oMSwgY291bnQpICU+JQogIGFycmFuZ2UoaWQpCgpwcmludChmYXZvdXJpdGVfZGF5X2hlaSkKYGBgCgpgYGB7cn0KbnVtYmVyX3Bvc3RzX2hvdXJzIDwtIGRhdGFfcG9zdHNfZGF5cyAlPiUKICBncm91cF9ieShpZCwgY3JlYXRlZF9ob3VyKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIHVuZ3JvdXAoKQoKZmF2b3VyaXRlX2hvdXJfaGVpIDwtIG51bWJlcl9wb3N0c19ob3VycyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgdG9wX24oMSwgY291bnQpICU+JQogIGFycmFuZ2UoaWQpCgpwcmludChmYXZvdXJpdGVfaG91cl9oZWkpCmBgYAoKIyBIZWF0bWFwcwoKIyBGdW5jdGlvbiB0byBwbG90IGhlYXRtYXAgZm9yIHZhcmlvdXMgSEVJcwoKYGBge3J9CmhlYXRtYXBfbWFrZXIgPC0gZnVuY3Rpb24odGFyZ2V0X2lkKXsKICAjIEZpbHRlcmluZyBkYXRhIGZvciB0aGUgc3BlY2lmaWMgSEVJCiAgdGFyZ2V0X2RhdGEgPC0gZGF0YV9wb3N0c19kYXlzICU+JQogICAgZmlsdGVyKGlkID09IHRhcmdldF9pZCkKICAKICAjIEdyb3VwaW5nIGJ5IGRheSBvZiB0aGUgd2VlayBhbmQgaG91ciwgYW5kIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgdHdlZXRzCiAgdHdlZXRfY291bnRzIDwtIHRhcmdldF9kYXRhICU+JQogICAgZ3JvdXBfYnkoZGF5X29mX3dlZWssIGNyZWF0ZWRfaG91cikgJT4lCiAgICBzdW1tYXJpc2UobnVtX3Bvc3RzID0gbigpKQogIAogICMgUGxvdHRpbmcgaGVhdG1hcAogIGdncGxvdCh0d2VldF9jb3VudHMsIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBjcmVhdGVkX2hvdXIsIGZpbGwgPSBudW1fcG9zdHMpKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiYmx1ZSIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiUG9zdCBIZWF0bWFwIGZvciIsIHRhcmdldF9pZCksCiAgICAgICAgIHggPSAiRGF5IG9mIHRoZSB3ZWVrIiwKICAgICAgICAgeSA9ICJIb3VyIG9mIHRoZSBkYXkiKQp9CmBgYAoKIyBQbG90IG9mIGhlYXRtYXAgZm9yIGVhY2ggSEVJCgpgYGB7cn0KaGVhdG1hcF9tYWtlcigiZHVrZS5jc3YiKQpoZWF0bWFwX21ha2VyKCJlcGZsLmNzdiIpCmhlYXRtYXBfbWFrZXIoImdvZS5jc3YiKQpoZWF0bWFwX21ha2VyKCJoYXJ2YXJkLmNzdiIpCmhlYXRtYXBfbWFrZXIoImxlaWNlc3Rlci5jc3YiKQpoZWF0bWFwX21ha2VyKCJtYW5jaGVzdGVyLmNzdiIpCmhlYXRtYXBfbWFrZXIoIm1pdC5jc3YiKQpoZWF0bWFwX21ha2VyKCJzYi5jc3YiKQpoZWF0bWFwX21ha2VyKCJzdGFuZm9yZC5jc3YiKQpoZWF0bWFwX21ha2VyKCJ0cmluaXR5LmNzdiIpCmhlYXRtYXBfbWFrZXIoInd2LmNzdiIpCmhlYXRtYXBfbWFrZXIoInlhbGUuY3N2IikKYGBgCgojIEhhc2h0YWdzCgpgYGB7cn0KIyBUcmFuc2Zvcm1pbmcgZW1wdHkgc3RyaW5ncyBpbnRvIE5BCmRhdGFfcG9zdHMkaGFzaHRhZ3NbZGF0YV9wb3N0cyRoYXNodGFncyA9PSAiIl0gPC0gTkEKCiMgVGFibGUgd2l0aCBudW1iZXIgb2YgdW5pcXVlIGhhc2h0YWdzIGFuZCBwZXJjZW50YWdlIG9mIHVzYWdlCmhhc2h0YWdzIDwtIGRhdGFfcG9zdHMgJT4lCiAgICAgICAgICAgICAgICBncm91cF9ieShpZCkgJT4lCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmEgPSBzdW0oaXMubmEoaGFzaHRhZ3MpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWVfaGFzaHRhZ3MgPSBsZW5ndGgodW5pcXVlKGhhc2h0YWdzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGFzaHRhZ19wZXJjZW50YWdlID0gcm91bmQoKChjb3VudCAtIG5hKSAvIGNvdW50ICogMTAwKSwgMikpCgpwcmludChoYXNodGFncykKYGBgCgojIFBsb3QgZm9yIHRoZSBjb3VudCBvZiB1bmlxdWUgaGFzaHRhZ3MgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdChoYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsCiAgICAgICAgbmFtZXMuYXJnID0gaGFzaHRhZ3MkaWQsCiAgICAgICAgbWFpbiA9ICJVbmlxdWUgSGFzaHRhZ3MgZm9yIEVhY2ggSEVJIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJDb3VudCBvZiBVbmlxdWUgSGFzaHRhZ3MiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChoYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MpICsgNTApLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sPSAiIzE2QTA4NSIpCgp0ZXh0KHggPSBiYXJwbG90KGhhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzLAogICAgIGxhYmVscyA9IHJvdW5kKGhhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSB1c2FnZSBvZiBoYXNodGFnIGZvciBlYWNoIEhFSQoKYGBge3J9CmJhcnBsb3QoaGFzaHRhZ3MkaGFzaHRhZ19wZXJjZW50YWdlLAogICAgICAgIG5hbWVzLmFyZyA9IGhhc2h0YWdzJGlkLAogICAgICAgIG1haW4gPSAiSGFzaHRhZ3MgUGVyY2VudGFnZSBmb3IgRWFjaCBIRUkiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkhhc2h0YWdzIFBlcmNlbnRhZ2UiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChoYXNodGFncyRoYXNodGFnX3BlcmNlbnRhZ2UpICsgMzApLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sPSAiI0YxQzQwRiIpCgp0ZXh0KHggPSBiYXJwbG90KGhhc2h0YWdzJGhhc2h0YWdfcGVyY2VudGFnZSwgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gaGFzaHRhZ3MkaGFzaHRhZ19wZXJjZW50YWdlLAogICAgIGxhYmVscyA9IHJvdW5kKGhhc2h0YWdzJGhhc2h0YWdfcGVyY2VudGFnZSwgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFVSTCB1c2FnZQoKYGBge3J9CiMgVHJhbnNmb3JtaW5nIGVtcHR5IHN0cmluZ3MgaW50byBOQQpkYXRhX3Bvc3RzJHVybHNbZGF0YV9wb3N0cyR1cmxzID09ICIiXSA8LSBOQQoKIyBUYWJsZSB3aXRoIG51bWJlciBvZiBwb3N0LCBudW1iZXIgb2YgTkEgYW5kIHVybCBwZXJjZW50YWdlIG9mIHVzYWdlCnVybF91c2FnZSA8LSBkYXRhX3Bvc3RzICU+JQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5hID0gc3VtKGlzLm5hKHVybHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfcGVyY2VudGFnZSA9IHJvdW5kKCgoY291bnQgLSBuYSkgLyBjb3VudCAqIDEwMCksIDIpKQoKcHJpbnQodXJsX3VzYWdlKQpgYGAKCiMgUGxvdCBmb3IgdGhlIHVzYWdlIG9mIGhhc2h0YWcgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdCh1cmxfdXNhZ2UkdXJsX3BlcmNlbnRhZ2UsCiAgICAgICAgbmFtZXMuYXJnID0gdXJsX3VzYWdlJGlkLAogICAgICAgIG1haW4gPSAiVXJscyBQZXJjZW50YWdlIGZvciBFYWNoIEhFSSIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiVXJscyBQZXJjZW50YWdlIiwKICAgICAgICB5bGltID0gYygwLCBtYXgodXJsX3VzYWdlJHVybF9wZXJjZW50YWdlKSArIDEwKSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbD0gIiM4RTQ0QUQiKQoKdGV4dCh4ID0gYmFycGxvdCh1cmxfdXNhZ2UkdXJsX3BlcmNlbnRhZ2UsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHVybF91c2FnZSR1cmxfcGVyY2VudGFnZSwKICAgICBsYWJlbHMgPSByb3VuZCh1cmxfdXNhZ2UkdXJsX3BlcmNlbnRhZ2UsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBUZXh0CgpgYGB7cn0KZGF0YV9wb3N0c19jb250ZW50IDwtIGRhdGFfcG9zdHMgJT4lCiAgICAgICAgICAgIHNlbGVjdChpZCwgdGV4dCkKCiMgQ291bnRpbmcgbnVtYmVyIG9mIHdvcmRzCmRhdGFfcG9zdHNfY29udGVudCA8LSBkYXRhX3Bvc3RzX2NvbnRlbnQgJT4lCiAgbXV0YXRlKG51bV93b3JkcyA9IGxlbmd0aHMoc3Ryc3BsaXQodGV4dCwgIlxccysiKSkpCgojIEdyb3VwaW5nIGJ5IEhFSSBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2UsIG1pbmltdW0sIGFuZCBtYXhpbXVtIHZhbHVlcyBvZiBudW1iZXIgb2Ygd29yZHMKZGF0YV9wb3N0c19jb250ZW50X21ldHJpY3MgPC0gZGF0YV9wb3N0c19jb250ZW50ICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9udW1fd29yZHMgPSBtZWFuKG51bV93b3JkcyksCiAgICAgICAgICAgIG1pbl9udW1fd29yZHMgPSBtaW4obnVtX3dvcmRzKSwKICAgICAgICAgICAgbWF4X251bV93b3JkcyA9IG1heChudW1fd29yZHMpKQpwcmludChkYXRhX3Bvc3RzX2NvbnRlbnRfbWV0cmljcykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlLCBtYXhpbXVtIGFuZCBtaW5pbXVtIHZhbHVlcyBvZiB3b3JkcyBmb3IgZWFjaCBIRUkKCmBgYHtyfQpnZ3Bsb3QoZGF0YV9wb3N0c19jb250ZW50X21ldHJpY3MsIGFlcyh4ID0gaWQsIHkgPSBhdmVyYWdlX251bV93b3JkcykpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9ICJBdmVyYWdlIikpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWluX251bV93b3JkcywgeW1heCA9IG1heF9udW1fd29yZHMsIGNvbG9yID0gIlJhbmdlIiksIHdpZHRoID0gMC4yKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkF2ZXJhZ2UiID0gIiMxOTc2RDIiLCAiUmFuZ2UiID0gIiNFRjUzNTAiKSkgKwogIGxhYnModGl0bGUgPSAiV29yZCBDb3VudCBTdW1tYXJ5IGJ5IEhFSSIsCiAgICAgICB4ID0gIkhFSSIsCiAgICAgICB5ID0gIk51bWJlciBvZiBXb3JkcyIsCiAgICAgICBjb2xvciA9ICJNZXRyaWMiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkKYGBgCgojIENsdXN0ZXJzCgpgYGB7cn0KIyBDcmVhdGluZyB0YWJsZSBmb3IgY2x1c3RlciBhbGdvcml0aG1zCgojIEpvaW5pbmcgYXR0cmlidXRlIHBlcmNlbnRhZ2VfdHdlZXRzIChwZXJjZW50YWdlIG9mIHR3ZWV0cyBvdXQgb2YgYWxsIHBvc3RzKSBhbmQgcGVyY2VudGFnZV9yZXBsaWVzIChwZXJjZW50YWdlIG9mIHJlcGxpZXMgb3V0IG9mIGFsbCBwb3N0cykgZnJvbSBudW1iZXJfcG9zdHMgYWxzbyBhZGRpbmcgdW5pcXVlX2hhc2h0YWdzIChudW1iZXIgb2YgdW5pcXVlIGhhc2h0YWdzKSBhbmQgaGFzaHRhZ19wZXJjZW50YWdlIChwZXJjZW50YWdlIG9mIHBvc3RzIHRoYXQgY29udGFpbiBhIGhhc2h0YWcpIGZyb20gaGFzaHRhZ3MsIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShzZWxlY3QoaGFzaHRhZ3MsIGlkLCB1bmlxdWVfaGFzaHRhZ3MsIGhhc2h0YWdfcGVyY2VudGFnZSksIHNlbGVjdChkYXRhX3JhdGlvLCBpZCwgcGVyY2VudGFnZV90d2VldHMsIHBlcmNlbnRhZ2VfcmVwbGllcyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z19wb3N0c19wZXJfZGF5cyAoYXZlcmFnZSBvZiBwb3N0cyBwZXIgZGF5KSBmcm9tIHBvc3RzX3Blcl9kYXkgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChwb3N0c19wZXJfZGF5LCBpZCwgYXZnX3Bvc3RzX3Blcl9kYXlzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3Bvc3RzX3Blcl93ZWVrcyAoYXZlcmFnZSBvZiBwb3N0cyBwZXIgd2VlaykgZnJvbSBwb3N0c19wZXJfd2VlayBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHBvc3RzX3Blcl93ZWVrLCBpZCwgYXZnX3Bvc3RzX3Blcl93ZWVrcyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z19wb3N0c19pbl9hY2FkZW1pY190aW1lIChhdmVyYWdlIG9mIHBvc3RzIGR1cmluZyBhY2FkZW1pYyB0aW1lKSAgZnJvbSBkYXRhX3Bvc3RzX2FjYWRlbWljIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoZGF0YV9wb3N0c19hY2FkZW1pYywgaWQsIGF2Z19wb3N0c19pbl9hY2FkZW1pY190aW1lKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3Bvc3RzX2luX3ZhY2F0aW9uX3RpbWUgKGF2ZXJhZ2Ugb2YgcG9zdHMgZHVyaW5nIHZhY2F0aW9uIHRpbWUpIGZyb20gZGF0YV9wb3N0c192YWNhdGlvbnMgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChkYXRhX3Bvc3RzX3ZhY2F0aW9ucywgaWQsIGF2Z19wb3N0c19pbl92YWNhdGlvbl90aW1lKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgY3JlYXRlZF9ob3VyIChob3VyIHdoZXJlIGV2ZXJ5IEhFSSBtYWRlIG1vcmUgcG9zdHMpIGZyb20gZmF2b3VyaXRlX2hvdXJfaGVpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoZmF2b3VyaXRlX2hvdXJfaGVpLCBpZCwgY3JlYXRlZF9ob3VyKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgdXJsX3BlcmNlbnRhZ2UgKHBlcmNlbnRhZ2Ugb2YgcG9zdHMgdGhhdCBjb250YWluIGFuIHVybCkgZnJvbSB1cmxfdXNhZ2UgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdCh1cmxfdXNhZ2UsIGlkLCB1cmxfcGVyY2VudGFnZSksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2ZXJhZ2VfbnVtX3dvcmRzIChhdmVyYWdlIG51bWJlciBvZiB3b3JkcyBpbiB0aGUgcG9zdHMpIGZyb20gZGF0YV9wb3N0c19jb250ZW50X21ldHJpY3MgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChkYXRhX3Bvc3RzX2NvbnRlbnRfbWV0cmljcywgaWQsIGF2ZXJhZ2VfbnVtX3dvcmRzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCnByaW50KGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBGdW5jdGlvbiBmb3IgY2x1c3RlciBtZXRob2QKCmBgYHtyfQpjbHVzdGVyX21ha2VyIDwtIGZ1bmN0aW9uKG51bV9jbHVzdGVycywgdGFibGUpewogICMgRXhjbHVkaW5nIGlkIGNvbHVtbiBmb3IgY2x1c3RlcmluZwogIGNsdXN0ZXJfZGF0YSA8LSBzZWxlY3QodGFibGUsIC1pZCkKICAgIAogICMgU2NhbGluZyB0aGUgZGF0YSBmb3Iga21lYW5zIG1ldGhvZAogIHNjYWxlZF9kYXRhIDwtIHNjYWxlKGNsdXN0ZXJfZGF0YSkKICAKICBrbWVhbnNfbW9kZWwgPC0ga21lYW5zKHNjYWxlZF9kYXRhLCBjZW50ZXJzID0gbnVtX2NsdXN0ZXJzLCBuc3RhcnQgPSAxMCkKCiAgIyBFeHRyYWN0IGNsdXN0ZXIgYXNzaWdubWVudHMKICBjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGttZWFuc19tb2RlbCRjbHVzdGVyCiAgCiAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGNvbWJpbmluZyBvcmlnaW5hbCBkYXRhIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cwogIGNsdXN0ZXJlZF9kYXRhIDwtIGNiaW5kKGNsdXN0ZXJfdGFibGUkaWQsIGNsdXN0ZXJfZGF0YSwgY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMpCiAgCiAgY2x1c3RlcmVkX2RhdGEgPC0gY2x1c3RlcmVkX2RhdGFbLCBjKCJjbHVzdGVyX3RhYmxlJGlkIiwgImNsdXN0ZXIiKV0KICAKICBwcmludChjbHVzdGVyZWRfZGF0YSkKfQpgYGAKCiMgRnVuY3Rpb24gdG8gZGlzY292ZXIgYmVzdCBudW1iZXIgb2YgY2x1c3RlcnMKCmBgYHtyfQplbGJvd19tYWtlciA8LSBmdW5jdGlvbih0YWJsZSl7CiAgY2x1c3Rlcl9kYXRhIDwtIHNlbGVjdCh0YWJsZSwgLWlkKQogIHNjYWxlZF9kYXRhIDwtIHNjYWxlKGNsdXN0ZXJfZGF0YSkKICAKICB3c3MgPC0gdmVjdG9yKCkKICByYW5nZSA8LSAxOjEwCiAgCiAgZm9yIChrIGluIHJhbmdlKSB7CiAgICBrbWVhbnNfbW9kZWwgPC0ga21lYW5zKHNjYWxlZF9kYXRhLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMTApCiAgICB3c3Nba10gPC0ga21lYW5zX21vZGVsJHRvdC53aXRoaW5zcwogIH0KICAKICBlbGJvd19kZiA8LSBkYXRhLmZyYW1lKGsgPSByYW5nZSwgV1NTID0gd3NzKQogIGdncGxvdChlbGJvd19kZiwgYWVzKHggPSBrLCB5ID0gV1NTKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGxhYnMoeCA9ICJOdW1iZXIgb2YgQ2x1c3RlcnMiLCB5ID0gIldpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzIChXQ1NTKSIsCiAgICAgICAgIHRpdGxlID0gIkVsYm93IE1ldGhvZCBmb3IgT3B0aW1hbCBrIikgKwogICAgdGhlbWVfbWluaW1hbCgpCn0KYGBgCgojIFBsb3Qgb2YgRWxib3cgTWV0aG9kIGFuZCBzZWxlY3Rpb24gb2YgYmVzdCBudW1iZXIgb2YgY2x1c3RlciB0byB2aWV3IGhvdyBIRUlzIGFyZSBncm91cGVkCgpgYGB7cn0KZWxib3dfbWFrZXIoY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9tYWtlcigzLCBjbHVzdGVyX3RhYmxlKQpgYGAKCiMgUG9zdHMgQ2xhc3NpZmljYXRpb24KCmBgYHtyfQpjbGVhbnVwIDwtIGZ1bmN0aW9uKGRvY3MsIHNwZWMud29yZHM9TlVMTCl7CiAgIyBsb3dlcmNhc2UKICBkb2NzIDwtIHRtX21hcChkb2NzLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQogICMgcm0gbnVtYmVycwogIGRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZU51bWJlcnMpCiAgIyBybSBlbmdsaXNoIGNvbW1vbiBzdG9wV29yZHMKICBkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCiAgIyBpZiBzdG9wd29yZHMgYXJlIHNwZWNpZmllZCBhcyBhIGNoYXJhY3RlciB2ZWN0b3IKICBpZighaXMubnVsbChzcGVjLndvcmRzKSkKICAgIGRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVdvcmRzLCBzcGVjLndvcmRzKQogICMgcm0gcHVuY3R1YXRpb25zCiAgZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlUHVuY3R1YXRpb24pCiAgIyBybSBleHRyYSB3aGl0ZSBzcGFjZXMKICBkb2NzIDwtIHRtX21hcChkb2NzLCBzdHJpcFdoaXRlc3BhY2UpCiAgIyBsZW1tYXRpemluZyB0ZXh0CiAgZG9jcyA8LSB0bV9tYXAoZG9jcywgbGVtbWF0aXplX3dvcmRzKQogIAogIGRvY3MKfQpgYGAKCmBgYHtyfQpwYWlyaW5nIDwtIGZ1bmN0aW9uKHdvcmRfZGljdCwgcGFpcmVkX3dvcmRzKSB7CiAgaWYgKG5yb3cod29yZF9kaWN0KSAhPSBsZW5ndGgocGFpcmVkX3dvcmRzKSkgewogICAgc3RvcCgiVGhlIG51bWJlciBvZiByb3dzIGluIHdvcmRfZGljdCBhbmQgdGhlIGxlbmd0aCBvZiBwYWlyZWRfd29yZHMgc2hvdWxkIGJlIGVxdWFsLiIpCiAgfQogIAogIHdvcmRfZGljdCA8LSB0aWJibGU6OnRpYmJsZSh3b3JkID0gd29yZF9kaWN0JHZhbHVlKQogIHJlc3VsdCA8LSB0aWJibGU6OnRpYmJsZSh3b3JkID0gd29yZF9kaWN0JHdvcmQsIGNhdGVnb3J5ID0gcGFpcmVkX3dvcmRzKQogIHJldHVybihyZXN1bHQpCn0KYGBgCgpgYGB7cn0KY2xhc3NpZnlfdGV4dCA8LSBmdW5jdGlvbih0ZXh0LCB3b3JkX3BhaXIpIHsKICAjIFRva2VuaXppbmcgdGhlIHRleHQgYW5kIGNvbnZlcnRpbmcgdG8gbG93ZXJjYXNlCiAgd29yZHMgPC0gdG9sb3dlcih1bmxpc3Qoc3Ryc3BsaXQodGV4dCwgIlxcVysiKSkpCiAgCiAgIyBGaW5kaW5nIHRoZSBmcmVxdWVudCB3b3JkcyBpbiB0aGUgdGV4dAogIGZyZXFfd29yZHMgPC0gd29yZHNbd29yZHMgJWluJSB3b3JkX3BhaXIkd29yZF0KICAKICBpZiAobGVuZ3RoKGZyZXFfd29yZHMpID09IDApIHsKICAgIHJldHVybigiVW5rbm93biIpICAjIFJldHVybmluZyBVbmtub3duIGlmIG5vIGZyZXF1ZW50IHdvcmRzIGFyZSBmb3VuZAogIH0KICAKICAjIEdldHRpbmcgdGhlIGNvcnJlc3BvbmRpbmcgY2F0ZWdvcmllcwogIGNhdGVnb3JpZXMgPC0gd29yZF9wYWlyJGNhdGVnb3J5W3dvcmRfcGFpciR3b3JkICVpbiUgZnJlcV93b3Jkc10KICAKICAjIFNvcnRpbmcgY2F0ZWdvcmllcyBieSBmcmVxdWVuY3kgaW4gZGVzY2VuZGluZyBvcmRlciBhbmQgcmV0dXJuIHRoZSBtb3N0IGZyZXF1ZW50IG9uZQogIHJldHVybihuYW1lcyhzb3J0KHRhYmxlKGNhdGVnb3JpZXMpLCBkZWNyZWFzaW5nID0gVFJVRSkpWzFdKQp9CmBgYAoKYGBge3J9CmNvcnB1c19tYWtlciA8LSBmdW5jdGlvbih0ZXh0KSB7CiAgdGV4dHMgPC0gdGV4dCR0ZXh0CiAgdmMgPC0gVmVjdG9yU291cmNlKHRleHRzKQogIGNvcnB1cyA8LSBDb3JwdXModmMpCiAgCiAgcmV0dXJuKGNvcnB1cykKfQpgYGAKCmBgYHtyfQpmcmVxX3Rlcm1zIDwtIGZ1bmN0aW9uKGNsZWFuX3RleHQsIG51bWJlcikgewogIGR0bSA8LSBEb2N1bWVudFRlcm1NYXRyaXgoY2xlYW5fdGV4dCkKICBkdG0udGZpZGYgPC0gd2VpZ2h0VGZJZGYoZHRtKQogIAogIG1kZiA8LSBhc190aWJibGUoYXMubWF0cml4KGR0bS50ZmlkZikpCiAgCiAgbWRmLmZyZXEgPC0gbWRmICU+JQogIHNlbGVjdChmaW5kRnJlcVRlcm1zKGR0bSwgbnVtYmVyKSkgJT4lCiAgc3VtbWFyaXNlX2FsbChzdW0pICU+JQogIGdhdGhlcigpICU+JQogIGFycmFuZ2UoZGVzYyh2YWx1ZSkpCgogIG1kZi5mcmVxJGtleSA8LSAKICAgIGZhY3RvcihtZGYuZnJlcSRrZXksCiAgICAgICAgICAgbGV2ZWxzID0gbWRmLmZyZXEka2V5W29yZGVyKG1kZi5mcmVxJHZhbHVlKV0pCiAgCiAgd29yZF9kaWN0aW9uYXJ5IDwtIGFzX3RpYmJsZShtZGYuZnJlcSRrZXkpCiAgCiAgZ2dwbG90KG1kZi5mcmVxLCBhZXMoeD1rZXksIHk9dmFsdWUpKSArCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICAgIGxhYnMoeD0idGVybXMiLCB5PSJmcmVxIikgKyBjb29yZF9mbGlwKCkKICAKICByZXR1cm4od29yZF9kaWN0aW9uYXJ5KQp9CmBgYAoKIyBEdWtlCgpgYGB7cn0KZHVrZV90ZXh0IDwtIHN1YnNldChkYXRhX3Bvc3RzX2NvbnRlbnQsIGlkID09ICJkdWtlLmNzdiIpCmR1a2VfY29ycHVzIDwtIGNvcnB1c19tYWtlcihkdWtlX3RleHQpCgpkdWtlX2NsZWFuX3RleHQgPC1jbGVhbnVwKGR1a2VfY29ycHVzLCBjKCJuZXciLCAid2lsbCIsICJjaGFuZ2UiLCAibm9ydGgiLCAiY2FuIiwgImZpcnN0IiwgInllYXIiLCAiY2Fyb2xpbmEiLCAieWVhcnMiLCAic3VtbWVyIiwgIm1lZXQiLCAib25lIikpCgpkdWtlX3dvcmRfZGljdGlvbmFyeSA8LSBmcmVxX3Rlcm1zKGR1a2VfY2xlYW5fdGV4dCwgMzApCgpkdWtlX3dvcmRfY2F0ZWdvcnkgPC0gYygiSW1hZ2UiLCAiRWR1Y2F0aW9uIiwgIkVkdWNhdGlvbiIsICJJbWFnZSIsICJJbWFnZSIsICJSZXNlYXJjaCIsICJSZXNlYXJjaCIsICJJbWFnZSIsICJJbWFnZSIsICJFbmdhZ2VtZW50IiwgIlJlc2VhcmNoIiwgIlJlc2VhcmNoIiwgIkltYWdlIiwgIkVkdWNhdGlvbiIsICJFZHVjYXRpb24iLCAiU29jaWV0eSIsICJFZHVjYXRpb24iLCAiRW5nYWdlbWVudCIpCgpkdWtlX3dvcmRfcGFpciA8LSBwYWlyaW5nKGR1a2Vfd29yZF9kaWN0aW9uYXJ5LCBkdWtlX3dvcmRfY2F0ZWdvcnkpCgpwcmludChkdWtlX3dvcmRfcGFpcikKCiMgQXBwbHlpbmcgdGhlIGNsYXNzaWZ5X3RleHQgZnVuY3Rpb24gdG8gZWFjaCB0ZXh0CmR1a2VfdGV4dCA8LSBkdWtlX3RleHQgJT4lCiAgbXV0YXRlKGNhdGVnb3J5ID0gc2FwcGx5KHRleHQsIGNsYXNzaWZ5X3RleHQsIHdvcmRfcGFpciA9IGR1a2Vfd29yZF9wYWlyKSkKCnByaW50KGR1a2VfdGV4dCkKYGBgCgojIGVwZmwKCmBgYHtyfQplcGZsX3RleHQgPC0gc3Vic2V0KGRhdGFfcG9zdHNfY29udGVudCwgaWQgPT0gImVwZmwuY3N2IikKZXBmbF9jb3JwdXMgPC0gY29ycHVzX21ha2VyKGVwZmxfdGV4dCkKCmVwZmxfY2xlYW5fdGV4dCA8LSBjbGVhbnVwKGVwZmxfY29ycHVzLCBjKCJuZXciLCAiYW1wIiwgImNhbiIsICJ3aWxsIiwgIm9uZSIsICJub3ciLCAi4oCTIiwgInZpYSIsICJwb3J0cmFpdCIpKQoKZXBmbF93b3JkX2RpY3Rpb25hcnkgPC0gZnJlcV90ZXJtcyhlcGZsX2NsZWFuX3RleHQsIDMwKQoKZXBmbF93b3JkX2NhdGVnb3J5IDwtIGMoIkltYWdlIiwgIlJlc2VhcmNoIiwgIkltYWdlIiwgIlJlc2VhcmNoIiwgIlJlc2VhcmNoIiwgIlJlc2VhcmNoIiwgIkVkdWNhdGlvbiIsICJJbWFnZSIsICJSZXNlYXJjaCIsICJSZXNlYXJjaCIsICJFZHVjYXRpb24iLCAiUmVzZWFyY2giLCBOQSwgIkVkdWNhdGlvbiIsICJFZHVjYXRpb24iKQoKZXBmbF93b3JkX3BhaXIgPC0gcGFpcmluZyhlcGZsX3dvcmRfZGljdGlvbmFyeSwgZXBmbF93b3JkX2NhdGVnb3J5KQoKcHJpbnQoZXBmbF93b3JkX3BhaXIpCgojIEFwcGx5aW5nIHRoZSBjbGFzc2lmeV90ZXh0IGZ1bmN0aW9uIHRvIGVhY2ggdGV4dAplcGZsX3RleHQgPC0gZXBmbF90ZXh0ICU+JQogIG11dGF0ZShjYXRlZ29yeSA9IHNhcHBseSh0ZXh0LCBjbGFzc2lmeV90ZXh0LCB3b3JkX3BhaXIgPSBlcGZsX3dvcmRfcGFpcikpCgpwcmludChlcGZsX3RleHQpCmBgYAoKIyBnb2UKCmBgYHtyfQpnb2VfdGV4dCA8LSBzdWJzZXQoZGF0YV9wb3N0c19jb250ZW50LCBpZCA9PSAiZ29lLmNzdiIpCmdvZV9jb3JwdXMgPC0gY29ycHVzX21ha2VyKGdvZV90ZXh0KQoKZ29lX2NsZWFuX3RleHQgPC0gY2xlYW51cChnb2VfY29ycHVzLCBjKCJjYW4iLCAibmV3IiwgImFtcCIsICJ3aWxsIikpCgpnb2Vfd29yZF9kaWN0aW9uYXJ5IDwtIGZyZXFfdGVybXMoZ29lX2NsZWFuX3RleHQsIDE1KQoKZ29lX3dvcmRfY2F0ZWdvcnkgPC0gYygiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiSW1hZ2UiLCAiRWR1Y2F0aW9uIiwgIkVuZ2FnZW1lbnQiLCAiSW1hZ2UiLCAiRW5nYWdlbWVudCIsICJFZHVjYXRpb24iKQoKZ29lX3dvcmRfcGFpciA8LSBwYWlyaW5nKGdvZV93b3JkX2RpY3Rpb25hcnksIGdvZV93b3JkX2NhdGVnb3J5KQoKcHJpbnQoZ29lX3dvcmRfcGFpcikKCiMgQXBwbHlpbmcgdGhlIGNsYXNzaWZ5X3RleHQgZnVuY3Rpb24gdG8gZWFjaCB0ZXh0CmdvZV90ZXh0IDwtIGdvZV90ZXh0ICU+JQogIG11dGF0ZShjYXRlZ29yeSA9IHNhcHBseSh0ZXh0LCBjbGFzc2lmeV90ZXh0LCB3b3JkX3BhaXIgPSBnb2Vfd29yZF9wYWlyKSkKCnByaW50KGdvZV90ZXh0KQpgYGAKCiMgaGFydmFyZAoKYGBge3J9CmhhcnZhcmRfdGV4dCA8LSBzdWJzZXQoZGF0YV9wb3N0c19jb250ZW50LCBpZCA9PSAiaGFydmFyZC5jc3YiKQpoYXJ2YXJkX2NvcnB1cyA8LSBjb3JwdXNfbWFrZXIoaGFydmFyZF90ZXh0KQoKaGFydmFyZF9jbGVhbl90ZXh0IDwtIGNsZWFudXAoaGFydmFyZF9jb3JwdXMsIGMoIm5ldyIsICJjYW4iLCAid2lsbCIsICJzdW1tZXIiLCAieWVhciIsICJmaXJzdCIsICJtYXkiLCAiLSIsICJyZWNlbnQiLCAieWVhcnMiLCAib25lIiwgInNhaWQiLCAidGltZSIsICJtYW55IiwgIndvcmxkIiwgImNoYW5nZSIpKQoKaGFydmFyZF93b3JkX2RpY3Rpb25hcnkgPC0gZnJlcV90ZXJtcyhoYXJ2YXJkX2NsZWFuX3RleHQsIDUwKQoKaGFydmFyZF93b3JkX2NhdGVnb3J5IDwtIGMoIkltYWdlIiwgIkVkdWNhdGlvbiIsICJSZXNlYXJjaCIsICJJbWFnZSIsICJSZXNlYXJjaCIsICJSZXNlYXJjaCIsICJFbmdhZ2VtZW50IiwgIkVkdWNhdGlvbiIsICJTb2NpZXR5IiwgIkVkdWNhdGlvbiIsICJTb2NpZXR5IiwgIlJlc2VhcmNoIiwgIkVkdWNhdGlvbiIsICJJbWFnZSIsICJSZXNlYXJjaCIsIE5BLCAiUmVzZWFyY2giLCAiRWR1Y2F0aW9uIiwgIkVkdWNhdGlvbiIsIE5BLCAiUmVzZWFyY2giLCAiU29jaWV0eSIsICJTb2NpZXR5IiwgIlJlc2VhcmNoIiwgIkVkdWNhdGlvbiIpCgpoYXJ2YXJkX3dvcmRfcGFpciA8LSBwYWlyaW5nKGhhcnZhcmRfd29yZF9kaWN0aW9uYXJ5LCBoYXJ2YXJkX3dvcmRfY2F0ZWdvcnkpCgpwcmludChoYXJ2YXJkX3dvcmRfcGFpcikKCmhhcnZhcmRfdGV4dCA8LSBoYXJ2YXJkX3RleHQgJT4lCiAgbXV0YXRlKGNhdGVnb3J5ID0gc2FwcGx5KHRleHQsIGNsYXNzaWZ5X3RleHQsIHdvcmRfcGFpciA9IGhhcnZhcmRfd29yZF9wYWlyKSkKCnByaW50KGhhcnZhcmRfdGV4dCkKYGBgCgojIGxlaWNlc3RlcgoKYGBge3J9CmxlaWNlc3Rlcl90ZXh0IDwtIHN1YnNldChkYXRhX3Bvc3RzX2NvbnRlbnQsIGlkID09ICJsZWljZXN0ZXIuY3N2IikKbGVpY2VzdGVyX2NvcnB1cyA8LSBjb3JwdXNfbWFrZXIobGVpY2VzdGVyX3RleHQpCgpsZWljZXN0ZXJfY2xlYW5fdGV4dCA8LSBjbGVhbnVwKGxlaWNlc3Rlcl9jb3JwdXMsIGMoIvCfkYkiLCAiZGF5IiwgIm5ldyIsICJjbGVhciIsICJ5ZWFyIiwgImNhbiIsICJ3aWxsIiwgInRpbWUiLCAic3BhY2UiLCAib25lIiwgImZpcnN0IikpCgpsZWljZXN0ZXJfd29yZF9kaWN0aW9uYXJ5IDwtIGZyZXFfdGVybXMobGVpY2VzdGVyX2NsZWFuX3RleHQsIDYwKQoKbGVpY2VzdGVyX3dvcmRfY2F0ZWdvcnkgPC0gYygiSW1hZ2UiLCBOQSwgIkltYWdlIiwgIkVuZ2FnZW1lbnQiLCAiU29jaWV0eSIsICJFZHVjYXRpb24iLCAiRWR1Y2F0aW9uIiwgIlJlc2VhcmNoIiwgIkVuZ2FnZW1lbnQiLCAiRW5nYWdlbWVudCIsICJJbWFnZSIsICJFbmdhZ2VtZW50IiwgIkVkdWNhdGlvbiIsICJFbmdhbWVtZW50IiwgIkVuZ2FnZW1lbnQiLCAiRWR1Y2F0aW9uIiwgIkltYWdlIiwgIkVuZ2FnZW1lbnQiLCAiRWR1Y2F0aW9uIiwgTkEsICJJbWFnZSIpCgpsZWljZXN0ZXJfd29yZF9wYWlyIDwtIHBhaXJpbmcobGVpY2VzdGVyX3dvcmRfZGljdGlvbmFyeSwgbGVpY2VzdGVyX3dvcmRfY2F0ZWdvcnkpCgpwcmludChsZWljZXN0ZXJfd29yZF9wYWlyKQoKbGVpY2VzdGVyX3RleHQgPC0gbGVpY2VzdGVyX3RleHQgJT4lCiAgbXV0YXRlKGNhdGVnb3J5ID0gc2FwcGx5KHRleHQsIGNsYXNzaWZ5X3RleHQsIHdvcmRfcGFpciA9IGxlaWNlc3Rlcl93b3JkX3BhaXIpKQoKcHJpbnQobGVpY2VzdGVyX3RleHQpCmBgYAoKIyBtYW5jaGVzdGVyCgpgYGB7cn0KbWFuY2hlc3Rlcl90ZXh0IDwtIHN1YnNldChkYXRhX3Bvc3RzX2NvbnRlbnQsIGlkID09ICJtYW5jaGVzdGVyLmNzdiIpCm1hbmNoZXN0ZXJfY29ycHVzIDwtIGNvcnB1c19tYWtlcihtYW5jaGVzdGVyX3RleHQpCgptYW5jaGVzdGVyX2NsZWFuX3RleHQgPC0gY2xlYW51cChtYW5jaGVzdGVyX2NvcnB1cywgYygi8J+RhyIsICJjYW4iLCAianVzdCIsICJ3aWxsIiwgImdldCIsICJ3ZWxsIiwgIm9uZSIsICJoZWxwIiwgIm5vdyIsICJuZXciLCAicmVhZCIsICJjb25ncmF0dWxhdGlvbnMiKSkKCm1hbmNoZXN0ZXJfd29yZF9kaWN0aW9uYXJ5IDwtIGZyZXFfdGVybXMobWFuY2hlc3Rlcl9jbGVhbl90ZXh0LCA1MCkKCm1hbmNoZXN0ZXJfd29yZF9jYXRlZ29yeSA8LSBjKE5BLCAiSW1hZ2UiLCAiRW5nYWdlbWVudCIsICJJbWFnZSIsICJFZHVjYXRpb24iLCAiRWR1Y2F0aW9uIiwgIkVuZ2FnZW1lbnQiLCAiSW1hZ2UiLCAiRWR1Y2F0aW9uIiwgIlNvY2lldHkiLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiRWR1Y2F0aW9uIiwgIkVkdWNhdGlvbiIsICJFZHVjYXRpb24iLCAiRW5nYWdlbWVudCIsICJSZXNlYXJjaCIsICJFZHVjYXRpb24iLCAiUmVzZWFyY2giKQoKbWFuY2hlc3Rlcl93b3JkX3BhaXIgPC0gcGFpcmluZyhtYW5jaGVzdGVyX3dvcmRfZGljdGlvbmFyeSwgbWFuY2hlc3Rlcl93b3JkX2NhdGVnb3J5KQoKcHJpbnQobWFuY2hlc3Rlcl93b3JkX3BhaXIpCgptYW5jaGVzdGVyX3RleHQgPC0gbWFuY2hlc3Rlcl90ZXh0ICU+JQogIG11dGF0ZShjYXRlZ29yeSA9IHNhcHBseSh0ZXh0LCBjbGFzc2lmeV90ZXh0LCB3b3JkX3BhaXIgPSBtYW5jaGVzdGVyX3dvcmRfcGFpcikpCgpwcmludChtYW5jaGVzdGVyX3RleHQpCmBgYAoKIyBtaXQKCmBgYHtyfQptaXRfdGV4dCA8LSBzdWJzZXQoZGF0YV9wb3N0c19jb250ZW50LCBpZCA9PSAibWl0LmNzdiIpCm1pdF9jb3JwdXMgPC0gY29ycHVzX21ha2VyKG1pdF90ZXh0KQoKbWl0X2NsZWFuX3RleHQgPC0gY2xlYW51cChtaXRfY29ycHVzLCBjKCJuZXciLCAiY2FuIiwgInNheXMiLCAi4oCcIiwgIuKAmSIsICLigJQiLCAiIiwgIm1heSIsICJmaXJzdCIsICJ3aWxsIiwgInVzaW5nIiwgIndheSIsICJvbmUiLCAic2NpZW5jZSIpKQoKbWl0X3dvcmRfZGljdGlvbmFyeSA8LSBmcmVxX3Rlcm1zKG1pdF9jbGVhbl90ZXh0LCA0MCkKCm1pdF93b3JkX2NhdGVnb3J5IDwtIGMoIkltYWdlIiwgIlJlc2VhcmNoIiwgTkEsICJJbWFnZSIsIE5BLCAiRWR1Y2F0aW9uIiwgIlJlc2VhcmNoIiwgTkEsICJFZHVjYXRpb24iLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiRWR1Y2F0aW9uIiwgIlJlc2VhcmNoIiwgIlJlc2VhcmNoIiwgIkltYWdlIiwgIkVkdWNhdGlvbiIsICJFZHVjYXRpb24iLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiUmVzZWFyY2giLCAiU29jaWV0eSIpCgptaXRfd29yZF9wYWlyIDwtIHBhaXJpbmcobWl0X3dvcmRfZGljdGlvbmFyeSwgbWl0X3dvcmRfY2F0ZWdvcnkpCgpwcmludChtaXRfd29yZF9wYWlyKQoKbWl0X3RleHQgPC0gbWl0X3RleHQgJT4lCiAgbXV0YXRlKGNhdGVnb3J5ID0gc2FwcGx5KHRleHQsIGNsYXNzaWZ5X3RleHQsIHdvcmRfcGFpciA9IG1pdF93b3JkX3BhaXIpKQoKcHJpbnQobWl0X3RleHQpCmBgYAoKIyBzYgoKYGBge3J9CnNiX3RleHQgPC0gc3Vic2V0KGRhdGFfcG9zdHNfY29udGVudCwgaWQgPT0gInNiLmNzdiIpCnNiX2NvcnB1cyA8LSBjb3JwdXNfbWFrZXIoc2JfdGV4dCkKCnNiX2NsZWFuX3RleHQgPC0gY2xlYW51cChzYl9jb3JwdXMsIGMoIm5ldyIsICLigJQiLCAid2lsbCIsICJ3ZWVrIiwgImNhbiIsICJhbXAiLCAidmlhIiwgIm5vdyIsICJmdXR1cmUiLCAiZmlyc3QiKSkKCnNiX3dvcmRfZGljdGlvbmFyeSA8LSBmcmVxX3Rlcm1zKHNiX2NsZWFuX3RleHQsIDMwKQoKc2Jfd29yZF9jYXRlZ29yeSA8LSBjKCJJbWFnZSIsICJSZXNlYXJjaCIsICJJbWFnZSIsICJJbWFnZSIsICJJbWFnZSIsIE5BLCAiUmVzZWFyY2giLCAiSW1hZ2UiLCAiSW1hZ2UiLCAiUmVzZWFyY2giLCAiRWR1Y2F0aW9uIiwgIkltYWdlIiwgIkVkdWNhdGlvbiIsICJJbWFnZSIsICJFZHVjYXRpb24iLCAiSW1hZ2UiLCAiU29jaWV0eSIpCgpzYl93b3JkX3BhaXIgPC0gcGFpcmluZyhzYl93b3JkX2RpY3Rpb25hcnksIHNiX3dvcmRfY2F0ZWdvcnkpCgpwcmludChzYl93b3JkX3BhaXIpCgpzYl90ZXh0IDwtIHNiX3RleHQgJT4lCiAgbXV0YXRlKGNhdGVnb3J5ID0gc2FwcGx5KHRleHQsIGNsYXNzaWZ5X3RleHQsIHdvcmRfcGFpciA9IHNiX3dvcmRfcGFpcikpCgpwcmludChzYl90ZXh0KQpgYGAKCiMgc3RhbmZvcmQKCmBgYHtyfQpzdGFuZm9yZF90ZXh0IDwtIHN1YnNldChkYXRhX3Bvc3RzX2NvbnRlbnQsIGlkID09ICJzdGFuZm9yZC5jc3YiKQpzdGFuZm9yZF9jb3JwdXMgPC0gY29ycHVzX21ha2VyKHN0YW5mb3JkX3RleHQpCgpzdGFuZm9yZF9jbGVhbl90ZXh0IDwtIGNsZWFudXAoc3RhbmZvcmRfY29ycHVzLCBjKCJuZXciLCAid2lsbCIpKQoKc3RhbmZvcmRfd29yZF9kaWN0aW9uYXJ5IDwtIGZyZXFfdGVybXMoc3RhbmZvcmRfY2xlYW5fdGV4dCwgMjUpCgpzdGFuZm9yZF93b3JkX2NhdGVnb3J5IDwtIGMoIkltYWdlIiwgIkltYWdlIiwgIlJlc2VhcmNoIiwgIkVkdWNhdGlvbiIsIE5BKQoKc3RhbmZvcmRfd29yZF9wYWlyIDwtIHBhaXJpbmcoc3RhbmZvcmRfd29yZF9kaWN0aW9uYXJ5LCBzdGFuZm9yZF93b3JkX2NhdGVnb3J5KQoKcHJpbnQoc3RhbmZvcmRfd29yZF9wYWlyKQoKc3RhbmZvcmRfdGV4dCA8LSBzdGFuZm9yZF90ZXh0ICU+JQogIG11dGF0ZShjYXRlZ29yeSA9IHNhcHBseSh0ZXh0LCBjbGFzc2lmeV90ZXh0LCB3b3JkX3BhaXIgPSBzdGFuZm9yZF93b3JkX3BhaXIpKQoKcHJpbnQoc3RhbmZvcmRfdGV4dCkKYGBgCgojIHRyaW5pdHkKCmBgYHtyfQp0cmluaXR5X3RleHQgPC0gc3Vic2V0KGRhdGFfcG9zdHNfY29udGVudCwgaWQgPT0gInRyaW5pdHkuY3N2IikKdHJpbml0eV9jb3JwdXMgPC0gY29ycHVzX21ha2VyKHRyaW5pdHlfdGV4dCkKCnRyaW5pdHlfY2xlYW5fdGV4dCA8LSBjbGVhbnVwKHRyaW5pdHlfY29ycHVzLCBjKCJhbXAiLCAicmVhZCIsICJuZXciLCAiY2FuIiwgIndpbGwiLCAid2VlayIsICJ3b3JrIiwgImdyZWF0IiwgImRheSIsICJ2aXNpdCIsICJpcmlzaCIsICJmaXJzdCIsICJsZWQiLCAiY29uZ3JhdHVsYXRpb25zIikpCgp0cmluaXR5X3dvcmRfZGljdGlvbmFyeSA8LSBmcmVxX3Rlcm1zKHRyaW5pdHlfY2xlYW5fdGV4dCwgNDApCgp0cmluaXR5X3dvcmRfY2F0ZWdvcnkgPC0gYygiSW1hZ2UiLCAiRWR1Y2F0aW9uIiwgIlJlc2VhcmNoIiwgIkltYWdlIiwgIlJlc2VhcmNoIiwgIkltYWdlIiwgIlJlc2VhcmNoIiwgIlNvY2lldHkiLCAiU29jaWV0eSIsICJFZHVjYXRpb24iLCAiRW5nYWdlbWVudCIsICJFbmdhZ2VtZW50IiwgIkVkdWNhdGlvbiIsICJJbWFnZSIsICJFZHVjYXRpb24iLCAiSW1hZ2UiLCAiUmVzZWFyY2giKQoKdHJpbml0eV93b3JkX3BhaXIgPC0gcGFpcmluZyh0cmluaXR5X3dvcmRfZGljdGlvbmFyeSwgdHJpbml0eV93b3JkX2NhdGVnb3J5KQoKcHJpbnQodHJpbml0eV93b3JkX3BhaXIpCgp0cmluaXR5X3RleHQgPC0gdHJpbml0eV90ZXh0ICU+JQogIG11dGF0ZShjYXRlZ29yeSA9IHNhcHBseSh0ZXh0LCBjbGFzc2lmeV90ZXh0LCB3b3JkX3BhaXIgPSB0cmluaXR5X3dvcmRfcGFpcikpCgpwcmludCh0cmluaXR5X3RleHQpCmBgYAoKIyB3dgoKYGBge3J9Cnd2X3RleHQgPC0gc3Vic2V0KGRhdGFfcG9zdHNfY29udGVudCwgaWQgPT0gInd2LmNzdiIpCnd2X2NvcnB1cyA8LSBjb3JwdXNfbWFrZXIod3ZfdGV4dCkKCnd2X2NsZWFuX3RleHQgPC0gY2xlYW51cCh3dl9jb3JwdXMsIGMoIvCfkpvwn5KZIiwgIvCfmYwiLCAi8J+RiSIsICJoYXBweSIsICJkYXkiLCAic2VlIiwgIndlZWsiLCAiZ3JlYXQiLCAiY2FuIiwgIndpbGwiLCAid2VsbCIsICJrbm93IiwgIm5ldyIsICJub3ciLCAiZ2V0IiwgImp1c3QiKSkKCnd2X3dvcmRfZGljdGlvbmFyeSA8LSBmcmVxX3Rlcm1zKHd2X2NsZWFuX3RleHQsIDQwKQoKd3Zfd29yZF9jYXRlZ29yeSA8LSBjKE5BLCAiSW1hZ2UiLCAiSW1hZ2UiLCBOQSwgIkltYWdlIiwgIkltYWdlIiwgIkVuZ2FnZW1lbnQiLCAiRWR1Y2F0aW9uIiwgIkVkdWNhdGlvbiIsICJFZHVjYXRpb24iLCBOQSkKCnd2X3dvcmRfcGFpciA8LSBwYWlyaW5nKHd2X3dvcmRfZGljdGlvbmFyeSwgd3Zfd29yZF9jYXRlZ29yeSkKCnByaW50KHd2X3dvcmRfcGFpcikKCnd2X3RleHQgPC0gd3ZfdGV4dCAlPiUKICBtdXRhdGUoY2F0ZWdvcnkgPSBzYXBwbHkodGV4dCwgY2xhc3NpZnlfdGV4dCwgd29yZF9wYWlyID0gd3Zfd29yZF9wYWlyKSkKCnByaW50KHd2X3RleHQpCmBgYAoKIyB5YWxlCgpgYGB7cn0KeWFsZV90ZXh0IDwtIHN1YnNldChkYXRhX3Bvc3RzX2NvbnRlbnQsIGlkID09ICJ5YWxlLmNzdiIpCnlhbGVfY29ycHVzIDwtIGNvcnB1c19tYWtlcih5YWxlX3RleHQpCgp5YWxlX2NsZWFuX3RleHQgPC0gY2xlYW51cCh5YWxlX2NvcnB1cywgYygibmV3IiwgIuKAlCIsICJ3aWxsIiwgImNhbiIsICInIiwgImZpcnN0IiwgIndvcmsiLCAicmVhZCIsICJoZWxwIiwgInllYXIiKSkKCnlhbGVfd29yZF9kaWN0aW9uYXJ5IDwtIGZyZXFfdGVybXMoeWFsZV9jbGVhbl90ZXh0LCA3MCkKCnlhbGVfd29yZF9jYXRlZ29yeSA8LSBjKCJSZXNlYXJjaCIsICJFZHVjYXRpb24iLCAiRWR1Y2F0aW9uIiwgTkEsICJSZXNlYXJjaCIsICJSZXNlYXJjaCIsICJSZXNlYXJjaCIsICJSZXNlYXJjaCIsICJFZHVjYXRpb24iLCAiSW1hZ2UiLCAiUmVzZWFyY2giLCAiRWR1Y2F0aW9uIiwgIkltYWdlIiwgIlJlc2VhcmNoIiwgTkEsICJSZXNlYXJjaCIsICJJbWFnZSIsICJJbWFnZSIsICJSZXNlYXJjaCIsICJJbWFnZSIsICJSZXNlYXJjaCIsICJTb2NpZXR5IiwgIlJlc2VhcmNoIiwgIlNvY2lldHkiLCAiU29jaWV0eSIpCgp5YWxlX3dvcmRfcGFpciA8LSBwYWlyaW5nKHlhbGVfd29yZF9kaWN0aW9uYXJ5LCB5YWxlX3dvcmRfY2F0ZWdvcnkpCgpwcmludCh5YWxlX3dvcmRfcGFpcikKCnlhbGVfdGV4dCA8LSB5YWxlX3RleHQgJT4lCiAgbXV0YXRlKGNhdGVnb3J5ID0gc2FwcGx5KHRleHQsIGNsYXNzaWZ5X3RleHQsIHdvcmRfcGFpciA9IHlhbGVfd29yZF9wYWlyKSkKCnByaW50KHlhbGVfdGV4dCkKYGBgCgojIFNlbnRpbWVudCBBbmFseXNpcwoKIyBGdW5jdGlvbiBmb3IgZW1vdGlvbnMKCmBgYHtyfQplbW90aW9uc19tYWtlciA8LSBmdW5jdGlvbih0ZXh0cywgaGVpX25hbWUpewogIHRleHRfZW1vdGlvbiA8LSBnZXRfbnJjX3NlbnRpbWVudCh0ZXh0cykKICAKICAjIFByb3BvcnRpb25zIGZvciB0ZXh0IHZhbHVlcyAKICBlbW90aW9uX3Byb3BvcnRpb25zIDwtIGNvbFN1bXMocHJvcC50YWJsZSh0ZXh0X2Vtb3Rpb25bLCAxOjhdKSkKICAKICAjIFZpc3VhbGl6YXRpb24gb2YgcGVyY2VudGFnZXMgb24gZWFjaCBlbW90aW9uIGZvdW5kIG9uIHBvc3RzCiAgYmFycGxvdCA8LSBiYXJwbG90KAogICAgc29ydChjb2xTdW1zKHByb3AudGFibGUodGV4dF9lbW90aW9uWywgMTo4XSkpKSwgCiAgICBob3JpeiA9IFRSVUUsIAogICAgY2V4Lm5hbWVzID0gMC43LCAKICAgIGxhcyA9IDEsIAogICAgbWFpbiA9IHNwcmludGYoIkVtb3Rpb25zIGZvdW5kIGluICVzJ3MgdGV4dHMiLCBoZWlfbmFtZSksIAogICAgeGxhYj0iUGVyY2VudGFnZSIsCiAgICB4bGltID0gYygwLCBtYXgoZW1vdGlvbl9wcm9wb3J0aW9ucykgKiAxLjIpCiAgKQogIAogIHRleHQoCiAgICB4ID0gc29ydChlbW90aW9uX3Byb3BvcnRpb25zKSwKICAgIHkgPSBiYXJwbG90LCAgICAgICAgICAgICAgICAgICAgCiAgICBsYWJlbHMgPSBzcHJpbnRmKCIlLjJmJSUiLCAxMDAgKiBzb3J0KGVtb3Rpb25fcHJvcG9ydGlvbnMpKSwKICAgIHBvcyA9IDQsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgY2V4ID0gMC43ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgKQp9CmBgYAoKIyBEdWtlCgpgYGB7cn0KZHVrZV90ZXh0JHNlbnRpbWVudCA8LSByb3VuZChnZXRfc2VudGltZW50KGR1a2VfdGV4dCR0ZXh0KSwgMikKCnByaW50KGR1a2VfdGV4dCkKYGBgCgpgYGB7cn0KZW1vdGlvbnNfbWFrZXIoZHVrZV90ZXh0JHRleHQsICJkdWtlIikKYGBgCgojIGVwZmwKCmBgYHtyfQplcGZsX3RleHQkc2VudGltZW50IDwtIHJvdW5kKGdldF9zZW50aW1lbnQoZXBmbF90ZXh0JHRleHQpLCAyKQoKcHJpbnQoZXBmbF90ZXh0KQpgYGAKCmBgYHtyfQplbW90aW9uc19tYWtlcihlcGZsX3RleHQkdGV4dCwgImVwZmwiKQpgYGAKCiMgZ29lCgpgYGB7cn0KZ29lX3RleHQkc2VudGltZW50IDwtIHJvdW5kKGdldF9zZW50aW1lbnQoZ29lX3RleHQkdGV4dCksIDIpCgpwcmludChnb2VfdGV4dCkKYGBgCgpgYGB7cn0KZW1vdGlvbnNfbWFrZXIoZ29lX3RleHQkdGV4dCwgImdvZSIpCmBgYAoKIyBoYXJ2YXJkCgpgYGB7cn0KaGFydmFyZF90ZXh0JHNlbnRpbWVudCA8LSByb3VuZChnZXRfc2VudGltZW50KGhhcnZhcmRfdGV4dCR0ZXh0KSwgMikKCnByaW50KGhhcnZhcmRfdGV4dCkKYGBgCgpgYGB7cn0KZW1vdGlvbnNfbWFrZXIoaGFydmFyZF90ZXh0JHRleHQsICJoYXJ2YXJkIikKYGBgCgojIGxlaWNlc3RlcgoKYGBge3J9CmxlaWNlc3Rlcl90ZXh0JHNlbnRpbWVudCA8LSByb3VuZChnZXRfc2VudGltZW50KGxlaWNlc3Rlcl90ZXh0JHRleHQpLCAyKQoKcHJpbnQobGVpY2VzdGVyX3RleHQpCmBgYAoKYGBge3J9CmVtb3Rpb25zX21ha2VyKGxlaWNlc3Rlcl90ZXh0JHRleHQsICJsZWljZXN0ZXIiKQpgYGAKCiMgbWFuY2hlc3RlcgoKYGBge3J9Cm1hbmNoZXN0ZXJfdGV4dCRzZW50aW1lbnQgPC0gcm91bmQoZ2V0X3NlbnRpbWVudChtYW5jaGVzdGVyX3RleHQkdGV4dCksIDIpCgpwcmludChtYW5jaGVzdGVyX3RleHQpCmBgYAoKYGBge3J9CmVtb3Rpb25zX21ha2VyKG1hbmNoZXN0ZXJfdGV4dCR0ZXh0LCAibWFuY2hlc3RlciIpCmBgYAoKIyBtaXQKCmBgYHtyfQptaXRfdGV4dCRzZW50aW1lbnQgPC0gcm91bmQoZ2V0X3NlbnRpbWVudChtaXRfdGV4dCR0ZXh0KSwgMikKCnByaW50KG1pdF90ZXh0KQpgYGAKCmBgYHtyfQplbW90aW9uc19tYWtlcihtaXRfdGV4dCR0ZXh0LCAibWl0IikKYGBgCgojIHNiCgpgYGB7cn0Kc2JfdGV4dCRzZW50aW1lbnQgPC0gcm91bmQoZ2V0X3NlbnRpbWVudChzYl90ZXh0JHRleHQpLCAyKQoKcHJpbnQoc2JfdGV4dCkKYGBgCgpgYGB7cn0KZW1vdGlvbnNfbWFrZXIoc2JfdGV4dCR0ZXh0LCAic2IiKQpgYGAKCiMgc3RhbmZvcmQKCmBgYHtyfQpzdGFuZm9yZF90ZXh0JHNlbnRpbWVudCA8LSByb3VuZChnZXRfc2VudGltZW50KHN0YW5mb3JkX3RleHQkdGV4dCksIDIpCgpwcmludChzdGFuZm9yZF90ZXh0KQpgYGAKCmBgYHtyfQplbW90aW9uc19tYWtlcihzdGFuZm9yZF90ZXh0JHRleHQsICJzdGFuZm9yZCIpCmBgYAoKIyB0cmluaXR5CgpgYGB7cn0KdHJpbml0eV90ZXh0JHNlbnRpbWVudCA8LSByb3VuZChnZXRfc2VudGltZW50KHRyaW5pdHlfdGV4dCR0ZXh0KSwgMikKCnByaW50KHRyaW5pdHlfdGV4dCkKYGBgCgpgYGB7cn0KZW1vdGlvbnNfbWFrZXIodHJpbml0eV90ZXh0JHRleHQsICJ0cmluaXR5IikKYGBgCgojIHd2CgpgYGB7cn0Kd3ZfdGV4dCRzZW50aW1lbnQgPC0gcm91bmQoZ2V0X3NlbnRpbWVudCh3dl90ZXh0JHRleHQpLCAyKQoKcHJpbnQod3ZfdGV4dCkKYGBgCgpgYGB7cn0KZW1vdGlvbnNfbWFrZXIod3ZfdGV4dCR0ZXh0LCAid3YiKQpgYGAKCiMgeWFsZQoKYGBge3J9CnlhbGVfdGV4dCRzZW50aW1lbnQgPC0gcm91bmQoZ2V0X3NlbnRpbWVudCh5YWxlX3RleHQkdGV4dCksIDIpCgpwcmludCh5YWxlX3RleHQpCmBgYAoKYGBge3J9CmVtb3Rpb25zX21ha2VyKHlhbGVfdGV4dCR0ZXh0LCAieWFsZSIpCmBgYA==